using DSPRE; using DSPRE.Resources; using DSPRE.ROMFiles; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; using static DSPRE.RomInfo; public static class ScriptDatabaseJsonLoader { /// /// Call this once at startup (before any script‐lookups). /// public static void InitializeFromJson(string jsonPath, GameVersions gameVersion) { string expandedPath = Environment.ExpandEnvironmentVariables(jsonPath); string text = File.ReadAllText(expandedPath); JsonDocument doc = JsonDocument.Parse(text); try { JsonElement root = doc.RootElement; ScriptDatabase.movementsDictIDName = root .GetProperty("movements") .EnumerateObject() .ToDictionary( prop => Convert.ToUInt16(prop.Name.Substring(2), 16), prop => prop.Value.GetString() ); ScriptDatabase.comparisonOperatorsDict = root .GetProperty("comparisonOperators") .EnumerateObject() .ToDictionary( prop => Convert.ToUInt16(prop.Name.Substring(2), 16), prop => prop.Value.GetString() ); ScriptDatabase.specialOverworlds = root .GetProperty("specialOverworlds") .EnumerateObject() .ToDictionary( prop => Convert.ToUInt16(prop.Name.Substring(2), 16), prop => prop.Value.GetString() ); ScriptDatabase.overworldDirections = root .GetProperty("overworldDirections") .EnumerateObject() .ToDictionary( prop => (byte)Convert.ToUInt16(prop.Name.Substring(2), 16), prop => prop.Value.GetString() ); Dictionary namesDict; Dictionary paramsDict; Dictionary soundsDict = ScriptDatabase.soundNames; switch (gameVersion) { case GameVersions.Platinum: namesDict = ScriptDatabase.PlatScrCmdNames; paramsDict = ScriptDatabase.PlatScrCmdParameters; break; case GameVersions.Diamond: case GameVersions.Pearl: namesDict = ScriptDatabase.DPScrCmdNames; paramsDict = ScriptDatabase.DPScrCmdParameters; break; case GameVersions.HeartGold: case GameVersions.SoulSilver: namesDict = ScriptDatabase.HGSSScrCmdNames; paramsDict = ScriptDatabase.HGSSScrCmdParameters; break; default: throw new ArgumentOutOfRangeException(nameof(gameVersion), gameVersion, "Unsupported game"); } JsonElement scrRoot; if (!root.TryGetProperty("scrcmd", out scrRoot)) throw new InvalidOperationException("JSON is missing the \"scrcmd\" key"); Console.WriteLine("About to load scrcmd entries:"); foreach (JsonProperty prop in scrRoot.EnumerateObject()) { ushort code = Convert.ToUInt16(prop.Name.Substring(2), 16); JsonElement entry = prop.Value; string name = entry.GetProperty("name").GetString(); namesDict[code] = name; var arr = entry.GetProperty("parameters"); byte[] bytes = arr .EnumerateArray() .Select(x => (byte)x.GetInt32()) .ToArray(); paramsDict[code] = bytes; } JsonElement soundsRoot; if (!root.TryGetProperty("sounds", out soundsRoot)) throw new InvalidOperationException("JSON is missing the \"sounds\" key"); foreach (JsonProperty prop in soundsRoot.EnumerateObject()) { if (ushort.TryParse(prop.Name, out ushort id)) { JsonElement entry = prop.Value; if (entry.TryGetProperty("name", out JsonElement nameElement)) { string name = nameElement.GetString(); if (!string.IsNullOrEmpty(name)) { soundsDict[id] = name; } } } } } finally { doc.Dispose(); } } public static void LoadParameterTypes(string jsonPath, GameVersions gameVersion) { Dictionary> paramtypesDict; switch (gameVersion) { case GameVersions.Platinum: paramtypesDict = ScriptDatabase.PlatScrCmdParameterTypes; break; case GameVersions.Diamond: case GameVersions.Pearl: paramtypesDict = ScriptDatabase.DPScrCmdParameterTypes; break; case GameVersions.HeartGold: case GameVersions.SoulSilver: paramtypesDict = ScriptDatabase.HGSSScrCmdParameterTypes; break; default: throw new ArgumentOutOfRangeException(nameof(gameVersion)); } string text = File.ReadAllText(jsonPath); JsonDocument doc = JsonDocument.Parse(text); try { JsonElement root = doc.RootElement; if (!root.TryGetProperty("scrcmd", out JsonElement scrRoot)) { throw new InvalidOperationException("JSON is missing the \"scrcmd\" key"); } foreach (JsonProperty prop in scrRoot.EnumerateObject()) { ushort code = Convert.ToUInt16(prop.Name.Substring(2), 16); JsonElement entry = prop.Value; if (entry.TryGetProperty("parameter_types", out JsonElement paramTypesElement)) { List paramTypes = new List(); foreach (JsonElement typeElement in paramTypesElement.EnumerateArray()) { string typeStr = typeElement.GetString(); if (!string.IsNullOrEmpty(typeStr)) { // Debug output to see what types we're getting Console.WriteLine($"Command 0x{code:X3} parameter type: {typeStr}"); var paramType = ScriptParameter.ParseTypeString(typeStr); paramTypes.Add(paramType); } } if (paramTypes.Count > 0) { paramtypesDict[code] = paramTypes; } } } } finally { doc.Dispose(); } } } namespace DSPRE.Resources { public static class ScriptDatabase { public static Dictionary comparisonOperatorsGenVappendix = new Dictionary() { /* GEN V ONLY */ [6] = "OR", [7] = "AND", [0xFF] = "TRUEUP" }; // will all be populated from json at runtime public static Dictionary comparisonOperatorsDict = new Dictionary(); public static Dictionary specialOverworlds = new Dictionary(); public static Dictionary overworldDirections = new Dictionary(); public static Dictionary DPScrCmdNames = new Dictionary(); public static Dictionary DPScrCmdParameters = new Dictionary(); public static Dictionary> DPScrCmdParameterTypes = new Dictionary>(); public static Dictionary PlatScrCmdNames = new Dictionary(); public static Dictionary PlatScrCmdParameters = new Dictionary(); public static Dictionary> PlatScrCmdParameterTypes = new Dictionary>(); public static Dictionary HGSSScrCmdNames = new Dictionary(); public static Dictionary HGSSScrCmdParameters = new Dictionary(); public static Dictionary> HGSSScrCmdParameterTypes = new Dictionary>(); public static Dictionary pokemonNames = new Dictionary(); public static Dictionary itemNames = new Dictionary(); public static Dictionary moveNames = new Dictionary(); public static Dictionary soundNames = new Dictionary(); public static Dictionary trainerNames = new Dictionary(); public static Dictionary movementsDictIDName = new Dictionary(); public static Dictionary commandsWithRelativeJump = new Dictionary() { //commandID, ID of parameter With Jump Address [0x0016] = 0, //Jump [0x0017] = 1, //Call [0x0018] = 1, //Call [0x0019] = 1, //Call [0x001A] = 0, //Call [0x001C] = 1, //JumpIf [0x001D] = 1, //CallIf [0x005E] = 1, //Movement }; public static HashSet endCodes = new HashSet() { 0x2, 0x16, 0x1B }; public static HashSet movementEndCodes = new HashSet() { 0x00FE, }; public static void InitializePokemonNames() { string[] names = GetPokemonNames(); pokemonNames = names.Select((name, index) => new { name, index }) .ToDictionary( x => (ushort)x.index, x => "SPECIES_" + x.name.ToUpper().Replace(' ', '_') ); } public static void InitializeItemNames() { string[] names = GetItemNames(); itemNames = names.Select((name, index) => new { name, index }) .ToDictionary( x => (ushort)x.index, x => "ITEM_" + x.name.ToUpper().Replace(' ', '_').Replace('É', 'E') ); } public static void InitializeMoveNames() { string[] names = GetAttackNames(); moveNames = names.Select((name, index) => new { name, index }) .ToDictionary( x => (ushort)x.index, x => "MOVE_" + x.name.ToUpper().Replace(' ', '_') ); } public static void InitializeTrainerNames() { string[] names = GetSimpleTrainerNames(); trainerNames = Enumerable.Range(0, names.Length) .ToDictionary( index => (ushort)index, index => index == 0 ? "TRAINER_NONE" : $"TRAINER_{names[index]}_{index:D3}" .ToUpper() .Replace(' ', '_') .Replace("&", "AND") ); } } }