diff --git a/EpinelPS.Analyzers/EpinelPS.Analyzers.csproj b/EpinelPS.Analyzers/EpinelPS.Analyzers.csproj
new file mode 100644
index 0000000..d12afce
--- /dev/null
+++ b/EpinelPS.Analyzers/EpinelPS.Analyzers.csproj
@@ -0,0 +1,20 @@
+
+
+
+ netstandard2.0
+ true
+ enable
+ preview
+ true
+ false
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
diff --git a/EpinelPS.Analyzers/LoadRecordInitializerGenerator.cs b/EpinelPS.Analyzers/LoadRecordInitializerGenerator.cs
new file mode 100644
index 0000000..ff96d37
--- /dev/null
+++ b/EpinelPS.Analyzers/LoadRecordInitializerGenerator.cs
@@ -0,0 +1,104 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Text;
+using System.Collections.Immutable;
+using System.Text;
+
+namespace EpinelPS.Analyzers;
+
+[Generator]
+public class LoadRecordInitializerGenerator : IIncrementalGenerator
+{
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // Step 1: Filter for field declarations with attributes
+ var fieldDeclarations = context.SyntaxProvider
+ .CreateSyntaxProvider(
+ predicate: static (node, _) => node is FieldDeclarationSyntax fds && fds.AttributeLists.Count > 0,
+ transform: static (ctx, _) => GetTargetFieldInfo(ctx)
+ )
+ .Where(static m => m is not null)
+ .Collect();
+
+
+ // Step 2: Generate the code
+ context.RegisterSourceOutput(fieldDeclarations, (spc, fieldInfos) =>
+ {
+ var source = GenerateInitializerCode(fieldInfos!);
+ spc.AddSource("GameDataInitializer.g.cs", SourceText.From(source, Encoding.UTF8));
+ });
+ }
+
+ private static LoadFieldInfo? GetTargetFieldInfo(GeneratorSyntaxContext context)
+ {
+ if (context.Node is not FieldDeclarationSyntax fieldDecl)
+ return null;
+
+ var variable = fieldDecl.Declaration.Variables.FirstOrDefault();
+ if (variable == null)
+ return null;
+
+ if (context.SemanticModel.GetDeclaredSymbol(variable) is not IFieldSymbol symbol)
+ return null;
+
+ foreach (var attr in symbol.GetAttributes())
+ {
+
+ if (attr.ConstructorArguments.Length == 3)
+ {
+ if (attr.ConstructorArguments[0].Value is not string fileName || attr.ConstructorArguments[1].Value is not string key || attr.ConstructorArguments[2].Value is not INamedTypeSymbol recordType)
+ return null;
+
+ return new LoadFieldInfo
+ {
+ ContainingClass = symbol.ContainingType.ToDisplayString(),
+ FieldName = symbol.Name,
+ FileName = fileName,
+ Key = key,
+ RecordTypeName = recordType.ToDisplayString()
+ };
+ }
+ }
+
+ return null;
+ }
+
+ private static string GenerateInitializerCode(ImmutableArray fieldInfos)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine("using System.Collections.Generic;");
+ sb.AppendLine("using System.Threading.Tasks;");
+ sb.AppendLine();
+ sb.AppendLine("namespace EpinelPS.Data;");
+ sb.AppendLine("public static class GameDataInitializer");
+ sb.AppendLine("{");
+ sb.AppendFormat($"public static int TotalFiles = {fieldInfos.Length};");
+ sb.AppendLine(" public static async Task InitializeGameData(IProgress progress = null)");
+ sb.AppendLine(" {");
+
+ foreach (var info in fieldInfos)
+ {
+ var tempVar = $"data_{info.FieldName}";
+ sb.AppendLine($" var {tempVar} = await {info.ContainingClass}.Instance.LoadZip<{info.RecordTypeName}>(\"{info.FileName}\", progress);");
+ sb.AppendLine($" foreach (var obj in {tempVar}.records)");
+ sb.AppendLine(" {");
+ sb.AppendLine($" {info.ContainingClass}.Instance.{info.FieldName}.Add(obj.{info.Key}, obj);");
+ sb.AppendLine(" }");
+ }
+
+ sb.AppendLine(" }");
+ sb.AppendLine("}");
+
+ return sb.ToString();
+ }
+
+ private class LoadFieldInfo
+ {
+ public string ContainingClass = "";
+ public string FieldName = "";
+ public string FileName = "";
+ public string Key = "";
+ public string RecordTypeName = "";
+ }
+}
\ No newline at end of file
diff --git a/EpinelPS.sln b/EpinelPS.sln
index 876297b..b0c1d05 100644
--- a/EpinelPS.sln
+++ b/EpinelPS.sln
@@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServerSelector.Desktop", "S
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Client", "Client", "{4BB2E77F-84A6-4644-9FB3-38E2A7DCDEC0}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EpinelPS.Analyzers", "EpinelPS.Analyzers\EpinelPS.Analyzers.csproj", "{E3B18A3D-B20B-447D-BCBE-12931E18B41E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -99,6 +101,30 @@ Global
{01D0A73A-A881-439D-9318-54DB5B00D6F5}.ReleaseDLL|x64.Build.0 = Release|Any CPU
{01D0A73A-A881-439D-9318-54DB5B00D6F5}.ReleaseDLL|x86.ActiveCfg = Release|Any CPU
{01D0A73A-A881-439D-9318-54DB5B00D6F5}.ReleaseDLL|x86.Build.0 = Release|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Debug|x64.Build.0 = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Debug|x86.Build.0 = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.DebugDLL|Any CPU.ActiveCfg = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.DebugDLL|Any CPU.Build.0 = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.DebugDLL|x64.ActiveCfg = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.DebugDLL|x64.Build.0 = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.DebugDLL|x86.ActiveCfg = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.DebugDLL|x86.Build.0 = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Release|x64.ActiveCfg = Release|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Release|x64.Build.0 = Release|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Release|x86.ActiveCfg = Release|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.Release|x86.Build.0 = Release|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.ReleaseDLL|Any CPU.ActiveCfg = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.ReleaseDLL|Any CPU.Build.0 = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.ReleaseDLL|x64.ActiveCfg = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.ReleaseDLL|x64.Build.0 = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.ReleaseDLL|x86.ActiveCfg = Debug|Any CPU
+ {E3B18A3D-B20B-447D-BCBE-12931E18B41E}.ReleaseDLL|x86.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/EpinelPS/Data/GameData.cs b/EpinelPS/Data/GameData.cs
index 23e3593..3dad3b4 100644
--- a/EpinelPS/Data/GameData.cs
+++ b/EpinelPS/Data/GameData.cs
@@ -23,60 +23,147 @@ namespace EpinelPS.Data
}
}
- private ZipFile MainZip;
- private MemoryStream ZipStream;
-
-
- private readonly Dictionary questDataRecords = [];
- private readonly Dictionary stageDataRecords = [];
- private readonly Dictionary rewardDataRecords = [];
- private readonly Dictionary userExpDataRecords = [];
- private readonly Dictionary chapterCampaignData = [];
- private readonly Dictionary characterCostumeTable = [];
- public readonly Dictionary characterTable = [];
- public readonly Dictionary tutorialTable = [];
- public readonly Dictionary itemEquipTable = [];
- public readonly Dictionary itemMaterialTable = [];
- public readonly Dictionary itemEquipExpTable = [];
- public readonly Dictionary ItemEquipGradeExpTable = [];
- private readonly Dictionary FieldMapData = [];
- private readonly Dictionary LevelData = [];
- private readonly Dictionary TacticAcademyLessons = [];
- public readonly Dictionary SidestoryRewardTable = [];
- public readonly Dictionary PositionReward = [];
- public readonly Dictionary FieldItems = [];
- public readonly Dictionary OutpostBattle = [];
- public readonly Dictionary jukeboxListDataRecords = [];
- private readonly Dictionary jukeboxThemeDataRecords = [];
- public readonly Dictionary gachaTypes = [];
- public readonly Dictionary eventManagers = [];
- public readonly Dictionary lwptablemgrs = [];
- public readonly Dictionary albumResourceRecords = [];
- public readonly Dictionary userFrameTable = [];
- public readonly Dictionary archiveRecordManagerTable = [];
- public readonly Dictionary archiveEventStoryRecords = [];
- public readonly Dictionary archiveEventQuestRecords = [];
- public readonly Dictionary archiveEventDungeonStageRecords = [];
- public readonly Dictionary userTitleRecords = [];
- public readonly Dictionary archiveMessengerConditionRecords = [];
- public readonly Dictionary characterStatTable = [];
- public readonly Dictionary skillInfoTable = [];
- public readonly Dictionary costTable = [];
- public readonly Dictionary mediasProductTable = [];
- public readonly Dictionary towerTable = [];
- public readonly Dictionary TriggerTable = [];
- public readonly Dictionary InfracoreTable = [];
- public readonly Dictionary AttractiveCounselCharacterTable = [];
- public readonly Dictionary AttractiveLevelReward = [];
- public readonly Dictionary Subquests = [];
- public readonly Dictionary Messages = [];
- public readonly Dictionary MessageConditions = [];
- public readonly Dictionary ScenarioRewards = [];
-
-
public byte[] Sha256Hash;
public int Size;
+ private ZipFile MainZip;
+ private MemoryStream ZipStream;
+ private int totalFiles = 1;
+ private int currentFile = 0;
+
+
+
+ [LoadRecord("MainQuestTable.json", "id", typeof(MainQuestCompletionTable))]
+ public readonly Dictionary QuestDataRecords = [];
+
+ [LoadRecord("CampaignStageTable.json", "id", typeof(CampaignStageTable))]
+ public readonly Dictionary StageDataRecords = [];
+
+ [LoadRecord("RewardTable.json", "id", typeof(RewardTable))]
+ public readonly Dictionary RewardDataRecords = [];
+
+ [LoadRecord("UserExpTable.json", "level", typeof(UserExpTable))]
+ public readonly Dictionary UserExpDataRecords = [];
+
+ [LoadRecord("CampaignChapterTable.json", "chapter", typeof(CampaignChapterTable))]
+ public readonly Dictionary ChapterCampaignData = [];
+
+ [LoadRecord("CharacterCostumeTable.json", "id", typeof(CharacterCostumeTable))]
+ public readonly Dictionary CharacterCostumeTable = [];
+
+ [LoadRecord("CharacterTable.json", "id", typeof(CharacterTable))]
+ public readonly Dictionary CharacterTable = [];
+
+ [LoadRecord("ContentsTutorialTable.json", "id", typeof(TutorialTable))]
+ public readonly Dictionary TutorialTable = [];
+ [LoadRecord("ItemEquipTable.json", "id", typeof(ItemEquipTable))]
+
+ public readonly Dictionary ItemEquipTable = [];
+
+ [LoadRecord("ItemMaterialTable.json", "id", typeof(ItemMaterialTable))]
+ public readonly Dictionary itemMaterialTable = [];
+
+ [LoadRecord("ItemEquipExpTable.json", "id", typeof(ItemEquipExpTable))]
+ public readonly Dictionary itemEquipExpTable = [];
+
+ [LoadRecord("ItemEquipGradeExpTable.json", "id", typeof(ItemEquipGradeExpTable))]
+ public readonly Dictionary ItemEquipGradeExpTable = [];
+
+ [LoadRecord("CharacterLevelTable.json", "level", typeof(CharacterLevelTable))]
+ public readonly Dictionary LevelData = [];
+
+ [LoadRecord("TacticAcademyFunctionTable.json", "id", typeof(TacticAcademyLessonTable))]
+ public readonly Dictionary TacticAcademyLessons = [];
+
+ [LoadRecord("SideStoryStageTable.json", "id", typeof(SideStoryStageTable))]
+ public readonly Dictionary SidestoryRewardTable = [];
+ public readonly Dictionary PositionReward = [];
+
+ [LoadRecord("FieldItemTable.json", "id", typeof(FieldItemTable))]
+ public readonly Dictionary FieldItems = [];
+
+ [LoadRecord("OutpostBattleTable.json", "id", typeof(OutpostBattleTable))]
+ public readonly Dictionary OutpostBattle = [];
+
+ [LoadRecord("JukeboxListTable.json", "id", typeof(JukeboxListTable))]
+ public readonly Dictionary jukeboxListDataRecords = [];
+
+ [LoadRecord("JukeboxThemeTable.json", "id", typeof(JukeboxThemeTable))]
+ public readonly Dictionary jukeboxThemeDataRecords = [];
+
+ [LoadRecord("GachaTypeTable.json", "id", typeof(GachaTypeTable))]
+ public readonly Dictionary gachaTypes = [];
+
+ [LoadRecord("EventManagerTable.json", "id", typeof(EventManagerTable))]
+ public readonly Dictionary eventManagers = [];
+
+ [LoadRecord("LiveWallpaperTable.json", "id", typeof(LiveWallpaperTable))]
+ public readonly Dictionary lwptablemgrs = [];
+
+ [LoadRecord("AlbumResourceTable.json", "id", typeof(AlbumResourceTable))]
+ public readonly Dictionary albumResourceRecords = [];
+
+ [LoadRecord("UserFrameTable.json", "id", typeof(UserFrameTable))]
+ public readonly Dictionary userFrameTable = [];
+
+ [LoadRecord("ArchiveRecordManagerTable.json", "id", typeof(ArchiveRecordManagerTable))]
+ public readonly Dictionary archiveRecordManagerTable = [];
+
+ [LoadRecord("ArchiveEventStoryTable.json", "id", typeof(ArchiveEventStoryTable))]
+ public readonly Dictionary archiveEventStoryRecords = [];
+
+ [LoadRecord("ArchiveEventQuestTable.json", "id", typeof(ArchiveEventQuestTable))]
+ public readonly Dictionary archiveEventQuestRecords = [];
+
+ [LoadRecord("ArchiveEventDungeonStageTable.json", "id", typeof(ArchiveEventDungeonStageTable))]
+ public readonly Dictionary archiveEventDungeonStageRecords = [];
+
+ [LoadRecord("UserTitleTable.json", "id", typeof(UserTitleTable))]
+ public readonly Dictionary userTitleRecords = [];
+
+ [LoadRecord("ArchiveMessengerConditionTable.json", "id", typeof(ArchiveMessengerConditionTable))]
+ public readonly Dictionary archiveMessengerConditionRecords = [];
+
+ [LoadRecord("CharacterStatTable.json", "id", typeof(CharacterStatTable))]
+ public readonly Dictionary characterStatTable = [];
+
+ [LoadRecord("SkillInfoTable.json", "id", typeof(SkillInfoTable))]
+ public readonly Dictionary skillInfoTable = [];
+
+ [LoadRecord("CostTable.json", "id", typeof(CostTable))]
+ public readonly Dictionary costTable = [];
+
+ [LoadRecord("MidasProductTable.json", "midas_product_id_proximabeta", typeof(MidasProductTable))]
+ public readonly Dictionary mediasProductTable = [];
+
+ [LoadRecord("TowerTable.json", "id", typeof(TowerTable))]
+ public readonly Dictionary towerTable = [];
+
+ [LoadRecord("TriggerTable.json", "id", typeof(TriggerTable))]
+ public readonly Dictionary TriggerTable = [];
+
+ [LoadRecord("InfraCoreGradeTable.json", "id", typeof(InfracoreTable))]
+ public readonly Dictionary InfracoreTable = [];
+
+ [LoadRecord("AttractiveCounselCharacterTable.json", "name_code", typeof(AttractiveCounselCharacterTable))]
+ public readonly Dictionary AttractiveCounselCharacterTable = [];
+
+ [LoadRecord("AttractiveLevelRewardTable.json", "id", typeof(AttractiveLevelRewardTable))]
+ public readonly Dictionary AttractiveLevelReward = [];
+
+ [LoadRecord("SubQuestTable.json", "id", typeof(SubquestTable))]
+ public readonly Dictionary Subquests = [];
+
+ [LoadRecord("MessengerDialogTable.json", "id", typeof(MessengerDialogTable))]
+ public readonly Dictionary Messages = [];
+
+ [LoadRecord("MessengerConditionTriggerTable.json", "id", typeof(MessengerMsgConditionTable))]
+ public readonly Dictionary MessageConditions = [];
+
+ [LoadRecord("ScenarioRewardsTable.json", "condition_id", typeof(ScenarioRewardTable))]
+ public readonly Dictionary ScenarioRewards = [];
+
+
static async Task BuildAsync()
{
await Load();
@@ -232,7 +319,7 @@ namespace EpinelPS.Data
}
#endregion
- private async Task LoadZip(string entry, ProgressBar bar)
+ public async Task LoadZip(string entry, IProgress bar)
{
var mainQuestData = MainZip.GetEntry(entry) ?? throw new Exception(entry + " does not exist in static data");
using StreamReader mainQuestReader = new(MainZip.GetInputStream(mainQuestData));
@@ -262,102 +349,13 @@ namespace EpinelPS.Data
return records;
}
- int totalFiles = 90;
- int currentFile = 0;
-
public async Task Parse()
{
using var progress = new ProgressBar();
- var questDataRecords = await LoadZip("MainQuestTable.json", progress);
- foreach (var obj in questDataRecords.records)
- {
- this.questDataRecords.Add(obj.id, obj);
- }
+ totalFiles = GameDataInitializer.TotalFiles;
- var stageDataRecords = await LoadZip("CampaignStageTable.json", progress);
- foreach (var obj in stageDataRecords.records)
- {
- this.stageDataRecords.Add(obj.id, obj);
- }
-
- var rewardDataRecords = await LoadZip("RewardTable.json", progress);
- foreach (var obj in rewardDataRecords.records)
- {
- this.rewardDataRecords.Add(obj.id, obj);
- }
-
- var chapterCampaignData = await LoadZip("CampaignChapterTable.json", progress);
- foreach (var obj in chapterCampaignData.records)
- {
- this.chapterCampaignData.Add(obj.chapter, obj);
- }
-
- var userExpDataRecords = await LoadZip("UserExpTable.json", progress);
- foreach (var obj in userExpDataRecords.records)
- {
- this.userExpDataRecords.Add(obj.level, obj);
- }
-
- var characterCostumeTable = await LoadZip("CharacterCostumeTable.json", progress);
- foreach (var obj in characterCostumeTable.records)
- {
- this.characterCostumeTable.Add(obj.id, obj);
- }
-
- var characterTable = await LoadZip("CharacterTable.json", progress);
- foreach (var obj in characterTable.records)
- {
- this.characterTable.Add(obj.id, obj);
- }
-
- var tutorialTable = await LoadZip("ContentsTutorialTable.json", progress);
- foreach (var obj in tutorialTable.records)
- {
- this.tutorialTable.Add(obj.id, obj);
- }
-
- var itemEquipTable = await LoadZip("ItemEquipTable.json", progress);
- foreach (var obj in itemEquipTable.records)
- {
- this.itemEquipTable.Add(obj.id, obj);
- }
-
- var itemMaterialTable = await LoadZip("ItemMaterialTable.json", progress);
- foreach (var obj in itemMaterialTable.records)
- {
- this.itemMaterialTable.Add(obj.id, obj);
- }
-
- var itemEquipExpTable = await LoadZip("ItemEquipExpTable.json", progress);
- foreach (var obj in itemEquipExpTable.records)
- {
- this.itemEquipExpTable.Add(obj.id, obj);
- }
-
- var ItemEquipGradeExpTable = await LoadZip("ItemEquipGradeExpTable.json", progress);
- foreach (var obj in ItemEquipGradeExpTable.records)
- {
- this.ItemEquipGradeExpTable.Add(obj.id, obj);
- }
-
- var characterLevelTable = await LoadZip("CharacterLevelTable.json", progress);
- foreach (var obj in characterLevelTable.records)
- {
- LevelData.Add(obj.level, obj);
- }
-
- var tacticLessonTable = await LoadZip("TacticAcademyFunctionTable.json", progress);
- foreach (var obj in tacticLessonTable.records)
- {
- TacticAcademyLessons.Add(obj.id, obj);
- }
-
- var sidestoryTable = await LoadZip("SideStoryStageTable.json", progress);
- foreach (var obj in sidestoryTable.records)
- {
- SidestoryRewardTable.Add(obj.id, obj);
- }
+ await GameDataInitializer.InitializeGameData(progress);
foreach (ZipEntry item in MainZip)
{
@@ -382,184 +380,12 @@ namespace EpinelPS.Data
}
}
}
- var fieldItems = await LoadZip("FieldItemTable.json", progress);
- foreach (var obj in fieldItems.records)
- {
- FieldItems.Add(obj.id, obj);
- }
- var battleOutpostTable = await LoadZip("OutpostBattleTable.json", progress);
- foreach (var obj in battleOutpostTable.records)
- {
- OutpostBattle.Add(obj.id, obj);
- }
-
- var archiveRecordManagerTableData = await LoadZip("ArchiveRecordManagerTable.json", progress);
- foreach (var obj in archiveRecordManagerTableData.records)
- {
- archiveRecordManagerTable.Add(obj.id, obj);
- }
-
- var gachaTypeTable = await LoadZip("GachaTypeTable.json", progress);
-
- // Add the records to the dictionary
- foreach (var obj in gachaTypeTable.records)
- {
- gachaTypes.Add(obj.id, obj); // Use obj.id as the key and obj (the GachaType) as the value
- }
-
- var eventManagerTable = await LoadZip("EventManagerTable.json", progress);
-
- // Add the records to the dictionary
- foreach (var obj in eventManagerTable.records)
- {
- eventManagers.Add(obj.id, obj); // Use obj.id as the key and obj (the EventManager) as the value
- }
-
- var lwptable = await LoadZip("LiveWallpaperTable.json", progress);
-
- // Add the records to the dictionary
- foreach (var obj in lwptable.records)
- {
- lwptablemgrs.Add(obj.id, obj); // Use obj.id as the key and obj (the LiveWallpaperRecord) as the value
- }
-
- var userFrameData = await LoadZip("UserFrameTable.json", progress);
- foreach (var record in userFrameData.records)
- {
- userFrameTable[record.id] = record;
- }
- // Load and parse ArchiveEventDungeonStageTable.json
- var archiveEventDungeonStageData = await LoadZip("ArchiveEventDungeonStageTable.json", progress);
- foreach (var obj in archiveEventDungeonStageData.records)
- {
- archiveEventDungeonStageRecords.Add(obj.id, obj);
- }
-
- var userTitleTable = await LoadZip("UserTitleTable.json", progress);
- foreach (var obj in userTitleTable.records)
- {
- userTitleRecords.Add(obj.id, obj);
- }
-
- // Load and parse ArchiveEventStoryTable.json
- var archiveEventStoryTable = await LoadZip("ArchiveEventStoryTable.json", progress);
- foreach (var obj in archiveEventStoryTable.records)
- {
- archiveEventStoryRecords.Add(obj.id, obj);
- }
-
- // Load and parse ArchiveEventQuestTable.json
- var archiveEventQuestTable = await LoadZip("ArchiveEventQuestTable.json", progress);
- foreach (var obj in archiveEventQuestTable.records)
- {
- archiveEventQuestRecords.Add(obj.id, obj);
- }
- // LOAD ARCHIVE MESSENGER CONDITION TABLE
- var archiveMessengerConditionTable = await LoadZip("ArchiveMessengerConditionTable.json", progress);
- foreach (var obj in archiveMessengerConditionTable.records)
- {
- archiveMessengerConditionRecords.Add(obj.id, obj);
- }
- var albumResourceTable = await LoadZip("AlbumResourceTable.json", progress);
- foreach (var obj in albumResourceTable.records)
- {
- albumResourceRecords.Add(obj.id, obj); // Now refers to the class-level field
- }
-
- var jukeboxListData = await LoadZip("JukeboxListTable.json", progress);
- foreach (var obj in jukeboxListData.records)
- {
- jukeboxListDataRecords.Add(obj.id, obj); // Now refers to the class-level field
- }
-
- var jukeboxThemeData = await LoadZip("JukeboxThemeTable.json", progress);
- foreach (var obj in jukeboxThemeData.records)
- {
- jukeboxThemeDataRecords.Add(obj.id, obj); // Now refers to the class-level field
- }
-
- var characterStatTable = await LoadZip("CharacterStatTable.json", progress);
- foreach (var obj in characterStatTable.records)
- {
- this.characterStatTable.Add(obj.id, obj);
- }
-
- var skillinfoTable = await LoadZip("SkillInfoTable.json", progress);
- foreach (var obj in skillinfoTable.records)
- {
- this.skillInfoTable.Add(obj.id, obj);
- }
-
- var costTable = await LoadZip("CostTable.json", progress);
- foreach (var obj in costTable.records)
- {
- this.costTable.Add(obj.id, obj);
- }
-
- var mediasProductTable = await LoadZip("MidasProductTable.json", progress);
- foreach (var obj in mediasProductTable.records)
- {
- this.mediasProductTable.Add(obj.midas_product_id_proximabeta, obj);
- }
-
- var towerTable = await LoadZip("TowerTable.json", progress);
- foreach (var obj in towerTable.records)
- {
- this.towerTable.Add(obj.id, obj);
- }
-
- var triggerTable = await LoadZip("TriggerTable.json", progress);
- foreach (var obj in triggerTable.records)
- {
- this.TriggerTable.Add(obj.id, obj);
- }
-
- var infracoreTable = await LoadZip("InfraCoreGradeTable.json", progress);
- foreach (var obj in infracoreTable.records)
- {
- this.InfracoreTable.Add(obj.id, obj);
- }
-
- var attrData = await LoadZip("AttractiveCounselCharacterTable.json", progress);
- foreach (var obj in attrData.records)
- {
- this.AttractiveCounselCharacterTable.Add(obj.name_code, obj);
- }
-
- var attrLData = await LoadZip("AttractiveLevelRewardTable.json", progress);
- foreach (var obj in attrLData.records)
- {
- this.AttractiveLevelReward.Add(obj.id, obj);
- }
-
- var subquest = await LoadZip("SubQuestTable.json", progress);
- foreach (var obj in subquest.records)
- {
- this.Subquests.Add(obj.id, obj);
- }
-
- var msgs = await LoadZip("MessengerDialogTable.json", progress);
- foreach (var obj in msgs.records)
- {
- this.Messages.Add(obj.id, obj);
- }
-
- var msgc = await LoadZip("MessengerConditionTriggerTable.json", progress);
- foreach (var obj in msgc.records)
- {
- this.MessageConditions.Add(obj.id, obj);
- }
-
- var scr = await LoadZip("ScenarioRewardsTable.json", progress);
- foreach (var obj in scr.records)
- {
- this.ScenarioRewards.Add(obj.condition_id, obj);
- }
+
}
public MainQuestCompletionRecord? GetMainQuestForStageClearCondition(int stage)
{
- foreach (var item in questDataRecords)
+ foreach (var item in QuestDataRecords)
{
if (item.Value.condition_id == stage)
{
@@ -571,15 +397,15 @@ namespace EpinelPS.Data
}
public MainQuestCompletionRecord? GetMainQuestByTableId(int tid)
{
- return questDataRecords[tid];
+ return QuestDataRecords[tid];
}
public CampaignStageRecord? GetStageData(int stage)
{
- return stageDataRecords[stage];
+ return StageDataRecords[stage];
}
public RewardTableRecord? GetRewardTableEntry(int rewardId)
{
- return rewardDataRecords[rewardId];
+ return RewardDataRecords[rewardId];
}
///
/// Returns the level and its minimum value for XP value
@@ -591,9 +417,9 @@ namespace EpinelPS.Data
{
int prevLevel = 0;
int prevValue = 0;
- for (int i = 1; i < userExpDataRecords.Count + 1; i++)
+ for (int i = 1; i < UserExpDataRecords.Count + 1; i++)
{
- var item = userExpDataRecords[i];
+ var item = UserExpDataRecords[i];
if (prevValue < targetExp)
{
@@ -609,9 +435,9 @@ namespace EpinelPS.Data
}
public int GetUserMinXpForLevel(int targetLevel)
{
- for (int i = 1; i < userExpDataRecords.Count + 1; i++)
+ for (int i = 1; i < UserExpDataRecords.Count + 1; i++)
{
- var item = userExpDataRecords[i];
+ var item = UserExpDataRecords[i];
if (targetLevel == item.level)
{
@@ -628,7 +454,7 @@ namespace EpinelPS.Data
{
string difficulty = keys[1];
- foreach (var item in chapterCampaignData)
+ foreach (var item in ChapterCampaignData)
{
if (difficulty == "Normal" && item.Value.chapter == chapterNum)
{
@@ -649,7 +475,7 @@ namespace EpinelPS.Data
}
public int GetNormalChapterNumberFromFieldName(string field)
{
- foreach (var item in chapterCampaignData)
+ foreach (var item in ChapterCampaignData)
{
if (item.Value.field_id == field)
{
@@ -659,14 +485,9 @@ namespace EpinelPS.Data
return -1;
}
-
- public IEnumerable GetAllCharacterTids()
- {
- return characterTable.Keys;
- }
public IEnumerable GetAllCostumes()
{
- foreach (var item in characterCostumeTable)
+ foreach (var item in CharacterCostumeTable)
{
yield return item.Value.id;
}
@@ -674,18 +495,18 @@ namespace EpinelPS.Data
internal ClearedTutorialData GetTutorialDataById(int TableId)
{
- return tutorialTable[TableId];
+ return TutorialTable[TableId];
}
public string? GetItemSubType(int itemType)
{
- return itemEquipTable[itemType].item_sub_type;
+ return ItemEquipTable[itemType].item_sub_type;
}
internal IEnumerable GetStageIdsForChapter(int chapterNumber, bool normal)
{
string mod = normal ? "Normal" : "Hard";
- foreach (var item in stageDataRecords)
+ foreach (var item in StageDataRecords)
{
var data = item.Value;
diff --git a/EpinelPS/Data/LoadRecordAttribute.cs b/EpinelPS/Data/LoadRecordAttribute.cs
new file mode 100644
index 0000000..43e5aa2
--- /dev/null
+++ b/EpinelPS/Data/LoadRecordAttribute.cs
@@ -0,0 +1,11 @@
+namespace EpinelPS.Data
+{
+ [System.AttributeUsage(System.AttributeTargets.Field)]
+
+ public class LoadRecordAttribute(string file, string primaryKey, Type tableType) : Attribute
+ {
+ public string File { get; set; } = file;
+ public string PrimaryKey { get; set; } = primaryKey;
+ public Type TableType { get; set; } = tableType;
+ }
+}
\ No newline at end of file
diff --git a/EpinelPS/Database/JsonDb.cs b/EpinelPS/Database/JsonDb.cs
index abef6f6..5052ae1 100644
--- a/EpinelPS/Database/JsonDb.cs
+++ b/EpinelPS/Database/JsonDb.cs
@@ -436,11 +436,11 @@ namespace EpinelPS.Database
public bool HasCharacter(int c)
{
// Step 1: Get the 'name_code' of the input character with Tid 'c'
- if (GameData.Instance.characterTable.TryGetValue(c, out var inputCharacterRecord))
+ if (GameData.Instance.CharacterTable.TryGetValue(c, out var inputCharacterRecord))
{
int targetNameCode = inputCharacterRecord.name_code;
// Step 2: Find all character IDs in 'characterTable' that have the same 'name_code'
- var matchingCharacterIds = GameData.Instance.characterTable.Where(kvp => kvp.Value.name_code == targetNameCode).Select(kvp => kvp.Key).ToHashSet();
+ var matchingCharacterIds = GameData.Instance.CharacterTable.Where(kvp => kvp.Value.name_code == targetNameCode).Select(kvp => kvp.Key).ToHashSet();
// Step 3: Check if any of your owned characters have a 'Tid' in the set of matching IDs
return Characters.Any(ownedCharacter => matchingCharacterIds.Contains(ownedCharacter.Tid));
@@ -455,11 +455,11 @@ namespace EpinelPS.Database
public Character? GetCharacter(int c)
{
// Step 1: Get the 'name_code' of the input character with Tid 'c'
- if (GameData.Instance.characterTable.TryGetValue(c, out var inputCharacterRecord))
+ if (GameData.Instance.CharacterTable.TryGetValue(c, out var inputCharacterRecord))
{
int targetNameCode = inputCharacterRecord.name_code;
// Step 2: Find all character IDs in 'characterTable' that have the same 'name_code'
- var matchingCharacterIds = GameData.Instance.characterTable.Where(kvp => kvp.Value.name_code == targetNameCode).Select(kvp => kvp.Key).ToHashSet();
+ var matchingCharacterIds = GameData.Instance.CharacterTable.Where(kvp => kvp.Value.name_code == targetNameCode).Select(kvp => kvp.Key).ToHashSet();
// Step 3: Check if any of your owned characters have a 'Tid' in the set of matching IDs
return Characters.Where(ownedCharacter => matchingCharacterIds.Contains(ownedCharacter.Tid)).First();
diff --git a/EpinelPS/EpinelPS.csproj b/EpinelPS/EpinelPS.csproj
index 91f6d36..2f549d1 100644
--- a/EpinelPS/EpinelPS.csproj
+++ b/EpinelPS/EpinelPS.csproj
@@ -12,20 +12,22 @@
$(NoWarn);SYSLIB0057
0.1.4.2
false
+ true
+
-
-
+
+
-
+
-
+
@@ -61,4 +63,8 @@
Always
+
+
+
+
diff --git a/EpinelPS/LobbyServer/Character/DoLimitBreak.cs b/EpinelPS/LobbyServer/Character/DoLimitBreak.cs
index 859897a..c46fc31 100644
--- a/EpinelPS/LobbyServer/Character/DoLimitBreak.cs
+++ b/EpinelPS/LobbyServer/Character/DoLimitBreak.cs
@@ -15,7 +15,7 @@ namespace EpinelPS.LobbyServer.Character
var user = GetUser();
// Get all character data from the game's character table
- var fullchardata = GameData.Instance.characterTable.Values.ToList();
+ var fullchardata = GameData.Instance.CharacterTable.Values.ToList();
var targetCharacter = user.GetCharacterBySerialNumber(req.Csn) ?? throw new NullReferenceException();
diff --git a/EpinelPS/LobbyServer/Character/SkillLevelUp.cs b/EpinelPS/LobbyServer/Character/SkillLevelUp.cs
index 6dff6dc..f38a10d 100644
--- a/EpinelPS/LobbyServer/Character/SkillLevelUp.cs
+++ b/EpinelPS/LobbyServer/Character/SkillLevelUp.cs
@@ -15,7 +15,7 @@ namespace EpinelPS.LobbyServer.Character
var character = user.Characters.FirstOrDefault(c => c.Csn == req.Csn) ?? throw new Exception("cannot find character");
- var charRecord = GameData.Instance.characterTable.Values.FirstOrDefault(c => c.id == character.Tid) ?? throw new Exception("cannot find character record");
+ var charRecord = GameData.Instance.CharacterTable.Values.FirstOrDefault(c => c.id == character.Tid) ?? throw new Exception("cannot find character record");
var skillIdMap = new Dictionary
{
{ 1, charRecord.ulti_skill_id },
diff --git a/EpinelPS/LobbyServer/Character/UpgradeCore.cs b/EpinelPS/LobbyServer/Character/UpgradeCore.cs
index 58be316..2e16496 100644
--- a/EpinelPS/LobbyServer/Character/UpgradeCore.cs
+++ b/EpinelPS/LobbyServer/Character/UpgradeCore.cs
@@ -16,7 +16,7 @@ namespace EpinelPS.LobbyServer.Character
var user = GetUser();
// Get all character data from the game's character table
- var fullchardata = GameData.Instance.characterTable.Values.ToList();
+ var fullchardata = GameData.Instance.CharacterTable.Values.ToList();
var targetCharacter = user.GetCharacterBySerialNumber(req.Csn) ?? throw new NullReferenceException();
diff --git a/EpinelPS/LobbyServer/Gacha/ExecGacha.cs b/EpinelPS/LobbyServer/Gacha/ExecGacha.cs
index 6fb7cfc..73a3451 100644
--- a/EpinelPS/LobbyServer/Gacha/ExecGacha.cs
+++ b/EpinelPS/LobbyServer/Gacha/ExecGacha.cs
@@ -30,7 +30,7 @@ namespace EpinelPS.LobbyServer.Gacha
var user = GetUser();
var response = new ResExecuteGacha() { Reward = new NetRewardData() { PassPoint = new() } };
- var entireallCharacterData = GameData.Instance.characterTable.Values.ToList();
+ var entireallCharacterData = GameData.Instance.CharacterTable.Values.ToList();
// Remove the .Values part since it's already a list.
// Group by name_code to treat same name_code as one character
// Always add characters with grade_core_id == 1 and 101
diff --git a/EpinelPS/LobbyServer/Gacha/ExecuteEventGacha.cs b/EpinelPS/LobbyServer/Gacha/ExecuteEventGacha.cs
index c46b977..3340253 100644
--- a/EpinelPS/LobbyServer/Gacha/ExecuteEventGacha.cs
+++ b/EpinelPS/LobbyServer/Gacha/ExecuteEventGacha.cs
@@ -26,7 +26,7 @@ namespace EpinelPS.LobbyServer.Gacha
var user = GetUser();
var response = new ResExecuteDailyFreeGacha();
- var entireallCharacterData = GameData.Instance.characterTable.Values.ToList();
+ var entireallCharacterData = GameData.Instance.CharacterTable.Values.ToList();
// Remove the .Values part since it's already a list.
// Group by name_code to treat same name_code as one character
// Always add characters with grade_core_id == 1 and 101
diff --git a/EpinelPS/LobbyServer/Inventory/IncreaseEquipmentExp.cs b/EpinelPS/LobbyServer/Inventory/IncreaseEquipmentExp.cs
index 562eaf6..cff5006 100644
--- a/EpinelPS/LobbyServer/Inventory/IncreaseEquipmentExp.cs
+++ b/EpinelPS/LobbyServer/Inventory/IncreaseEquipmentExp.cs
@@ -68,8 +68,8 @@ namespace EpinelPS.LobbyServer.Inventory
(int exp, int modules) AddExp(NetItemData srcItem, ItemData destItem)
{
int[] maxLevel = [0, 0, 3, 3, 4, 4, 5, 5, 5, 5];
- var srcEquipRecord = GameData.Instance.itemEquipTable.Values.FirstOrDefault(x => x.id == srcItem.Tid);
- var destEquipRecord = GameData.Instance.itemEquipTable.Values.FirstOrDefault(x => x.id == destItem.ItemType) ?? throw new NullReferenceException();;
+ var srcEquipRecord = GameData.Instance.ItemEquipTable.Values.FirstOrDefault(x => x.id == srcItem.Tid);
+ var destEquipRecord = GameData.Instance.ItemEquipTable.Values.FirstOrDefault(x => x.id == destItem.ItemType) ?? throw new NullReferenceException();;
int[] expNextTable = [.. GameData.Instance.itemEquipExpTable.Values
.Where(x => x.item_rare == destEquipRecord.item_rare)
.Select(x => x.exp)
@@ -120,7 +120,7 @@ namespace EpinelPS.LobbyServer.Inventory
private int GetSourceExp(NetItemData srcItem)
{
var item = GetUser().Items.FirstOrDefault(x => x.Isn == srcItem.Isn) ?? throw new NullReferenceException();
- var equipRecord = GameData.Instance.itemEquipTable.Values.FirstOrDefault(x => x.id == item.ItemType) ?? throw new NullReferenceException();
+ var equipRecord = GameData.Instance.ItemEquipTable.Values.FirstOrDefault(x => x.id == item.ItemType) ?? throw new NullReferenceException();
var levelRecord = GameData.Instance.ItemEquipGradeExpTable.Values.FirstOrDefault(x => x.grade_core_id == equipRecord.grade_core_id);
int[] expNextTable = GameData.Instance.itemEquipExpTable.Values
.Where(x => x.item_rare == equipRecord.item_rare)
@@ -140,7 +140,7 @@ namespace EpinelPS.LobbyServer.Inventory
private static int CalcTotalExp(ItemData destItem)
{
int exp = 0;
- var equipRecord = GameData.Instance.itemEquipTable.Values.FirstOrDefault(x => x.id == destItem.ItemType) ?? throw new NullReferenceException();
+ var equipRecord = GameData.Instance.ItemEquipTable.Values.FirstOrDefault(x => x.id == destItem.ItemType) ?? throw new NullReferenceException();
var levelRecord = GameData.Instance.ItemEquipGradeExpTable.Values.FirstOrDefault(x => x.grade_core_id == equipRecord.grade_core_id);
int[] expNextTable = GameData.Instance.itemEquipExpTable.Values
.Where(x => x.item_rare == equipRecord.item_rare)
diff --git a/EpinelPS/Program.cs b/EpinelPS/Program.cs
index 490e0ee..fef4a3c 100644
--- a/EpinelPS/Program.cs
+++ b/EpinelPS/Program.cs
@@ -282,7 +282,7 @@ namespace EpinelPS
else
{
// Group characters by name_code and always add those with grade_core_id == 11, 103, and include grade_core_id == 201
- var allCharacters = GameData.Instance.characterTable.Values
+ var allCharacters = GameData.Instance.CharacterTable.Values
.GroupBy(c => c.name_code) // Group by name_code to treat same name_code as one character 3999 = marian
.SelectMany(g => g.Where(c => c.grade_core_id == 1 || c.grade_core_id == 101 || c.grade_core_id == 201 || c.name_code == 3999)) // Always add characters with grade_core_id == 11 and 103
.ToList();
@@ -381,7 +381,7 @@ namespace EpinelPS
}
else
{
- foreach (var tutorial in GameData.Instance.tutorialTable.Values)
+ foreach (var tutorial in GameData.Instance.TutorialTable.Values)
{
if (!user.ClearedTutorialData.ContainsKey(tutorial.id))
{
@@ -417,7 +417,7 @@ namespace EpinelPS
int tid = character.Tid;
// Get the character data from the character table
- if (!GameData.Instance.characterTable.TryGetValue(tid, out var charData))
+ if (!GameData.Instance.CharacterTable.TryGetValue(tid, out var charData))
{
Console.WriteLine($"Character data not found for Tid {tid}");
continue;
@@ -445,7 +445,7 @@ namespace EpinelPS
int newGradeCoreId = Math.Min(inputGrade + 1, maxGradeCoreId); // +1 because inputGrade starts from 0 for SSRs
// Find the character with the same name_code and new grade_core_id
- var newCharData = GameData.Instance.characterTable.Values.FirstOrDefault(c =>
+ var newCharData = GameData.Instance.CharacterTable.Values.FirstOrDefault(c =>
c.name_code == nameCode && c.grade_core_id == newGradeCoreId);
if (newCharData != null)
@@ -466,7 +466,7 @@ namespace EpinelPS
int newGradeCoreId = Math.Min(101 + inputGrade, maxGradeCoreId); // Starts at 101
// Find the character with the same name_code and new grade_core_id
- var newCharData = GameData.Instance.characterTable.Values.FirstOrDefault(c =>
+ var newCharData = GameData.Instance.CharacterTable.Values.FirstOrDefault(c =>
c.name_code == nameCode && c.grade_core_id == newGradeCoreId);
if (newCharData != null)
diff --git a/EpinelPS/Utils/FormulaUtils.cs b/EpinelPS/Utils/FormulaUtils.cs
index e864694..f08480c 100644
--- a/EpinelPS/Utils/FormulaUtils.cs
+++ b/EpinelPS/Utils/FormulaUtils.cs
@@ -9,7 +9,7 @@ namespace EpinelPS.Utils
var character = user.Characters.FirstOrDefault(c => c.Csn == csn);
if (character == null) return 0;
- var charRecord = GameData.Instance.characterTable.Values.FirstOrDefault(c => c.id == character.Tid);
+ var charRecord = GameData.Instance.CharacterTable.Values.FirstOrDefault(c => c.id == character.Tid);
if (charRecord == null) return 0;
var statRecord = GameData.Instance.characterStatTable.Values.FirstOrDefault(s => charRecord.stat_enhance_id == s.group + (character.Level - 1));