Files
CustomizeTool/CustomizePlus/Profiles/ProfileManager.ProfileLoading.cs
RisaDev 2d40fff844 More work on profile character assignment rewrite.
Added ability to apply profile to any currently logged in character
Functional UI for player character, retainers and mannequins
Almost completely switched to using ActorIdentifier instead of character name
Migration code for ActorIdentifier instead of character names
IPC is not functional for now (see todos)
2024-10-07 01:11:20 +03:00

154 lines
5.5 KiB
C#

using CustomizePlus.Profiles.Data;
using CustomizePlus.Profiles.Events;
using CustomizePlus.Templates.Data;
using CustomizePlus.Templates;
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.GameData.Actors;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Penumbra.String;
using Penumbra.GameData.Structs;
namespace CustomizePlus.Profiles;
public partial class ProfileManager : IDisposable
{
public void LoadProfiles()
{
_logger.Information("Loading profiles...");
//todo: hot reload was not tested
//save temp profiles
var temporaryProfiles = Profiles.Where(x => x.IsTemporary).ToList();
Profiles.Clear();
List<(Profile, string)> invalidNames = new();
foreach (var file in _saveService.FileNames.Profiles())
{
_logger.Debug($"Reading profile {file.FullName}");
try
{
var text = File.ReadAllText(file.FullName);
var data = JObject.Parse(text);
var profile = LoadIndividualProfile(data);
if (profile.UniqueId.ToString() != Path.GetFileNameWithoutExtension(file.Name))
invalidNames.Add((profile, file.FullName));
if (Profiles.Any(f => f.UniqueId == profile.UniqueId))
throw new Exception($"ID {profile.UniqueId} was not unique.");
Profiles.Add(profile);
}
catch (Exception ex)
{
_logger.Error($"Could not load profile, skipped:\n{ex}");
//++skipped;
}
}
foreach (var profile in Profiles)
{
//This will solve any issues if file on disk was manually edited and we have more than a single active profile
if (profile.Enabled)
SetEnabled(profile, true, true);
if (_configuration.DefaultProfile == profile.UniqueId)
DefaultProfile = profile;
}
//insert temp profiles back into profile list
if (temporaryProfiles.Count > 0)
{
Profiles.AddRange(temporaryProfiles);
Profiles.Sort((x, y) => y.IsTemporary.CompareTo(x.IsTemporary));
}
var failed = MoveInvalidNames(invalidNames);
if (invalidNames.Count > 0)
_logger.Information(
$"Moved {invalidNames.Count - failed} profiles to correct names.{(failed > 0 ? $" Failed to move {failed} profiles to correct names." : string.Empty)}");
_logger.Information("Profiles load complete");
_event.Invoke(ProfileChanged.Type.ReloadedAll, null, null);
}
private Profile LoadIndividualProfile(JObject obj)
{
var version = obj["Version"]?.ToObject<int>() ?? 0;
return version switch
{
//Ignore everything below v4
4 => LoadV4(obj),
5 => LoadV5(obj),
_ => throw new Exception("The profile to be loaded has no valid Version."),
};
}
private Profile LoadV4(JObject obj)
{
var profile = LoadProfileV4V5(obj);
profile.CharacterName = new LowerString(obj["CharacterName"]?.ToObject<string>()?.Trim() ?? throw new ArgumentNullException("CharacterName"));
return profile;
}
private Profile LoadV5(JObject obj)
{
var profile = LoadProfileV4V5(obj);
var character = _actorManager.FromJson(obj["Character"] as JObject);
profile.Character = character;
profile.ApplyToCurrentlyActiveCharacter = obj["ApplyToCurrentlyActiveCharacter"]?.ToObject<bool>() ?? false;
profile.CharacterName = new LowerString(obj["CharacterName"]?.ToObject<string>()?.Trim() ?? throw new ArgumentNullException("CharacterName")); //temp
return profile;
}
//V4 and V5 are mostly not different, so common loading logic is here
private Profile LoadProfileV4V5(JObject obj)
{
var creationDate = obj["CreationDate"]?.ToObject<DateTimeOffset>() ?? throw new ArgumentNullException("CreationDate");
var profile = new Profile()
{
CreationDate = creationDate,
UniqueId = obj["UniqueId"]?.ToObject<Guid>() ?? throw new ArgumentNullException("UniqueId"),
Name = new LowerString(obj["Name"]?.ToObject<string>()?.Trim() ?? throw new ArgumentNullException("Name")),
LimitLookupToOwnedObjects = obj["LimitLookupToOwnedObjects"]?.ToObject<bool>() ?? throw new ArgumentNullException("LimitLookupToOwnedObjects"),
Enabled = obj["Enabled"]?.ToObject<bool>() ?? throw new ArgumentNullException("Enabled"),
ModifiedDate = obj["ModifiedDate"]?.ToObject<DateTimeOffset>() ?? creationDate,
IsWriteProtected = obj["IsWriteProtected"]?.ToObject<bool>() ?? false,
Templates = new List<Template>()
};
if (profile.ModifiedDate < creationDate)
profile.ModifiedDate = creationDate;
if (obj["Templates"] is not JArray templateArray)
return profile;
foreach (var templateObj in templateArray)
{
if (templateObj is not JObject templateObjCast)
{
//todo: warning
continue;
}
var templateId = templateObjCast["TemplateId"]?.ToObject<Guid>();
if (templateId == null)
continue; //todo: error
var template = _templateManager.GetTemplate((Guid)templateId);
if (template != null)
profile.Templates.Add(template);
}
return profile;
}
}