using System; using System.IO; using System.Reflection; using System.Resources; using System.Windows.Forms; using System.Linq; using DSPRE.ROMFiles; using System.Collections.Generic; using DSPRE.Resources.ROMToolboxDB; using DSPRE.Resources; using static DSPRE.RomInfo; namespace DSPRE { public partial class ROMToolboxDialog : Form { internal class ARM9PatchData { internal string initString; internal string branchString; internal uint branchOffset; internal uint initOffset; internal ARM9PatchData() { branchOffset = ToolboxDB.arm9ExpansionOffsetsDB[nameof(branchOffset) + "_" + RomInfo.gameFamily] - 0x02000000; initOffset = ToolboxDB.arm9ExpansionOffsetsDB[nameof(initOffset) + "_" + RomInfo.gameFamily + "_" + RomInfo.gameLanguage] - 0x02000000; branchString = ToolboxDB.arm9ExpansionCodeDB[nameof(branchString) + "_" + RomInfo.gameFamily + "_" + RomInfo.gameLanguage]; if (RomInfo.gameFamily == "Plat" ) { initString = ToolboxDB.arm9ExpansionCodeDB[nameof(initString) + "_" + RomInfo.gameFamily + "_" + RomInfo.gameLanguage]; } else { initString = ToolboxDB.arm9ExpansionCodeDB[nameof(initString) + "_" + RomInfo.gameFamily]; } } } internal class BDHCAMPatchData { internal byte overlayNumber; internal uint branchOffset; internal string branchString; internal uint overlayOffset1; internal uint overlayOffset2; internal string overlayString1; internal string overlayString2; internal byte[] subroutine; internal BDHCAMPatchData () { switch (RomInfo.gameFamily) { case "Plat": overlayNumber = 5; branchString = ToolboxDB.BDHCamCodeDB[nameof(branchString) + "_" + RomInfo.gameFamily + "_" + RomInfo.gameLanguage]; branchOffset = ToolboxDB.BDHCamOffsetsDB[nameof(branchOffset) + "_" + RomInfo.gameFamily + "_" + RomInfo.gameLanguage]; overlayOffset1 = ToolboxDB.BDHCamOffsetsDB[nameof(overlayOffset1) + "_" + RomInfo.gameFamily + "_" + RomInfo.gameLanguage]; overlayOffset2 = ToolboxDB.BDHCamOffsetsDB[nameof(overlayOffset2) + "_" + RomInfo.gameFamily + "_" + RomInfo.gameLanguage]; break; case "HGSS": overlayNumber = 1; branchString = ToolboxDB.BDHCamCodeDB[nameof(branchString) + "_" + RomInfo.gameFamily]; branchOffset = ToolboxDB.BDHCamOffsetsDB[nameof(branchOffset) + "_" + RomInfo.gameFamily]; overlayOffset1 = ToolboxDB.BDHCamOffsetsDB[nameof(overlayOffset1) + "_" + RomInfo.gameFamily]; overlayOffset2 = ToolboxDB.BDHCamOffsetsDB[nameof(overlayOffset2) + "_" + RomInfo.gameFamily]; break; } branchOffset -= 0x02000000; overlayString1 = ToolboxDB.BDHCamCodeDB[nameof(overlayString1)]; overlayString2 = ToolboxDB.BDHCamCodeDB[nameof(overlayString2)]; subroutine = (byte[])new ResourceManager("DSPRE.Resources.ROMToolboxDB.BDHCAMPatchDB", Assembly.GetExecutingAssembly()).GetObject(RomInfo.romID + "_cam"); } } public uint expandedARMfileID = ToolboxDB.syntheticOverlayFileNumbersDB[RomInfo.gameFamily]; public static bool flag_standardizedItems { get; private set; } = false; public static bool flag_arm9Expanded { get; private set; } = false; public static bool flag_BDHCamPatchApplied { get; private set; } = false; public static bool flag_MatrixExpansionApplied { get; private set; } = false; public static bool overlay1MustBeRestoredFromBackup { get; private set; } = true; #region Constructor public ROMToolboxDialog() { InitializeComponent(); CheckStandardizedItems(); if (RomInfo.gameLanguage == "ENG" || RomInfo.gameLanguage == "ESP") { CheckARM9Expansion(); } else { DisableARM9patch("Unsupported\nlanguage"); DisableBDHCamPatch("Unsupported\nlanguage"); DisableScrcmdRepointPatch("Unsupported\nlanguage"); } switch (RomInfo.gameFamily) { case "DP": DisableOverlay1patch("Unsupported"); DisableMatrixExpansionPatch("Unsupported"); DisableScrcmdRepointPatch("Unsupported"); break; case "Plat": DisableOverlay1patch("Unsupported"); DisableMatrixExpansionPatch("Unsupported"); DisableScrcmdRepointPatch("Unsupported"); CheckFilesBDHCamPatchApplied(); break; case "HGSS": if (!DSUtils.CheckOverlayHasCompressionFlag(1)) { DisableOverlay1patch("Already applied"); overlay1CB.Visible = true; } CheckFilesBDHCamPatchApplied(); if (RomInfo.gameLanguage == "ENG" || RomInfo.gameLanguage == "ESP") { CheckMatrixPatchApplied(); CheckScrcmdRepointPatchApplied(); } else { DisableMatrixExpansionPatch("Unsupported\nlanguage"); DisableScrcmdRepointPatch("Unsupported\nlanguage"); } break; } } #region Patch Disable private void DisableOverlay1patch(string reason) { overlay1uncomprButton.Enabled = false; overlay1uncompressedLBL.Enabled = false; overlay1patchtextLBL.Enabled = false; overlay1uncomprButton.Text = reason; } private void DisableBDHCamPatch(string reason) { BDHCamPatchButton.Enabled = false; BDHCamPatchLBL.Enabled = false; BDHCamPatchTextLBL.Enabled = false; BDHCamARM9requiredLBL.Enabled = false; BDHCamPatchButton.Text = reason; } private void DisableARM9patch(string reason) { applyARM9ExpansionButton.Enabled = false; arm9expansionTextLBL.Enabled = false; arm9expansionLBL.Enabled = false; applyARM9ExpansionButton.Text = reason; } private void DisableMatrixExpansionPatch(string reason) { expandMatrixButton.Enabled = false; matrixExpansionLBL.Enabled = false; matrixExpansionTextLBL.Enabled = false; expandMatrixButton.Text = reason; } private void DisableStandardizeItemsPatch(string reason) { applyItemStandardizeButton.Enabled = false; standardizePatchLBL.Enabled = false; standardizePatchTextLBL.Enabled = false; applyItemStandardizeButton.Text = reason; } private void DisableScrcmdRepointPatch(string reason) { repointScrcmdButton.Enabled = false; repointScrcmdLBL.Enabled = false; repointScrcmdTextLBL.Enabled = false; scrcmdARM9requiredLBL.Enabled = false; repointScrcmdButton.Text = reason; } #endregion #endregion #region Patch Checkers private int CheckARM9Expansion() { if (!flag_arm9Expanded) { ARM9PatchData data = new ARM9PatchData(); try { byte[] branchCode = HexStringToByteArray(data.branchString); byte[] branchCodeRead = DSUtils.ReadFromArm9(data.branchOffset, data.branchString.Length / 3 + 1); //Read branchCode if (branchCodeRead.Length != branchCode.Length) return 0; //0 means ARM9 Expansion has never been applied if (!branchCodeRead.SequenceEqual(branchCode)) return 0; byte[] initCode = HexStringToByteArray(data.initString); byte[] initCodeRead = DSUtils.ReadFromArm9(data.initOffset, data.initString.Length / 3 + 1); //Read initCode if (initCodeRead.Length != initCode.Length) return 0; if (!initCodeRead.SequenceEqual(initCode)) return 0; } catch { return -1; //-1 means Check failure } } flag_arm9Expanded = true; arm9patchCB.Visible = true; DisableARM9patch("Already applied"); switch (RomInfo.gameFamily) { case "Plat": case "HGSS": BDHCamARM9requiredLBL.Visible = false; BDHCamPatchButton.Enabled = true; BDHCamPatchLBL.Enabled = true; BDHCamPatchTextLBL.Enabled = true; break; } return 1; //arm9 Expansion has already been applied } private int CheckFilesBDHCamPatchApplied() { if (!flag_arm9Expanded) { BDHCamARM9requiredLBL.Visible = true; DisableBDHCamPatch("!"); return 0; } if (!flag_BDHCamPatchApplied) { BDHCAMPatchData data = new BDHCAMPatchData(); try { byte[] branchCode = HexStringToByteArray(data.branchString); byte[] branchCodeRead = DSUtils.ReadFromArm9(data.branchOffset, branchCode.Length); if (branchCode.Length != branchCodeRead.Length) return 0; //0 means BDHCAM patch has not been applied if (!branchCode.SequenceEqual(branchCodeRead)) return 0; string overlayFilePath = RomInfo.workDir + "overlay" + "\\" + "overlay_" + data.overlayNumber.ToString("D4") + ".bin"; DSUtils.DecompressOverlay(data.overlayNumber, true); byte[] overlayCode1 = HexStringToByteArray(data.overlayString1); byte[] overlayCode1Read = DSUtils.ReadFromFile(overlayFilePath, data.overlayOffset1, overlayCode1.Length); if (overlayCode1.Length != overlayCode1Read.Length) return 0; //0 means BDHCAM patch has not been applied if (!overlayCode1.SequenceEqual(overlayCode1Read)) return 0; byte[] overlayCode2 = HexStringToByteArray(data.overlayString2); byte[] overlayCode2Read = DSUtils.ReadFromFile(overlayFilePath, data.overlayOffset2, overlayCode2.Length); //Write new overlayCode1 if (overlayCode2.Length != overlayCode2Read.Length) return 0; //0 means BDHCAM patch has not been applied if (!overlayCode2.SequenceEqual(overlayCode2Read)) return 0; String fullFilePath = RomInfo.gameDirs[DirNames.synthOverlay].unpackedDir + '\\' + expandedARMfileID.ToString("D4"); byte[] subroutineRead = DSUtils.ReadFromFile(fullFilePath, ToolboxDB.BDHCamSubroutineOffset, data.subroutine.Length); //Write new overlayCode1 if (data.subroutine.Length != subroutineRead.Length) return 0; //0 means BDHCAM patch has not been applied if (!data.subroutine.SequenceEqual(subroutineRead)) return 0; } catch { return -1; } } flag_BDHCamPatchApplied = true; BDHCamCB.Visible = true; DisableBDHCamPatch("Already applied"); return 0; } public bool CheckStandardizedItems() { DSUtils.TryUnpackNarcs(new List { RomInfo.DirNames.scripts }); if ( flag_standardizedItems || MainProgram.ScanScriptsCheckStandardizedItemNumbers() ) { itemNumbersCB.Visible = true; flag_standardizedItems = true; DisableStandardizeItemsPatch("Already applied"); return true; } return false; } private int CheckMatrixPatchApplied() { if (!flag_MatrixExpansionApplied) { try { foreach (KeyValuePair kv in ToolboxDB.matrixExpansionDB) { foreach (uint offset in kv.Key) { int languageOffset = 0; if (RomInfo.romID == "IPKE" || RomInfo.romID == "IPGE" || RomInfo.romID == "IPGS") languageOffset = +8; byte[] read = DSUtils.ReadFromArm9((uint)(offset - 0x02000000 + languageOffset), kv.Value.Length / 3 + 1); byte[] code = HexStringToByteArray(kv.Value); if (read.Length != code.Length) return 0; if (!read.SequenceEqual(code)) return 0; } } } catch { return -1; //-1 means Check failure } } DisableMatrixExpansionPatch("Already applied"); flag_MatrixExpansionApplied = true; expandedMatrixCB.Visible = true; return 1; //arm9 Expansion has already been applied } private void CheckScrcmdRepointPatchApplied() { //throw new NotImplementedException(); } #endregion #region Button Actions private void SentenceCasePatchButton_Click(object sender, EventArgs e) { DialogResult d; d = MessageBox.Show("Confirming this process will apply the following changes:\n\n" + "- Every Pokémon name will be converted to Sentence Case." + "\n\n" + "Do you wish to continue?", "Confirm to proceed", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (d == DialogResult.Yes) { foreach (int ID in RomInfo.pokemonNamesTextNumbers) { TextArchive pokeName = new TextArchive(ID); for(ushort i = 1; i < pokeName.messages.Count; i++) { if (pokeName.messages[i].Length <= 1) i++; pokeName.messages[i] = pokeName.messages[i].Replace(PokeDatabase.System.pokeNames[i].ToUpper(), PokeDatabase.System.pokeNames[i]); } pokeName.SaveToFileDefaultDir(ID, showSuccessMessage: false); } //sentenceCaseCB.Visible = true; MessageBox.Show("Pokémon names have been converted to Sentence Case.", "Operation successful", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("No changes have been made.", "Operation canceled", MessageBoxButtons.OK, MessageBoxIcon.Information); } } private void BDHCAMPatchButton_Click(object sender, EventArgs e) { BDHCAMPatchData data = new BDHCAMPatchData(); if (RomInfo.gameFamily == "HGSS") { if (DSUtils.CheckOverlayHasCompressionFlag(data.overlayNumber)) { DialogResult d1; d1 = MessageBox.Show("It is STRONGLY recommended to configure Overlay1 as uncompressed before proceeding.\n\n" + "More details in the following dialog.\n\n" + "Do you want to know more?", "Confirm to proceed", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (d1 == DialogResult.Yes) { overlay1uncomprButton_Click(null, null); } } } DialogResult d2; d2 = MessageBox.Show("This process will apply the following changes:\n\n" + "- Backup ARM9 file (arm9.bin.backup will be created)." + "\n\n" + "- Backup Overlay" + data.overlayNumber + " file (overlay" + data.overlayNumber + ".bin.backup will be created)." + "\n\n" + "- Replace " + (data.branchString.Length / 3 + 1) + " bytes of data at arm9 offset 0x" + data.branchOffset.ToString("X") + " with " + '\n' + data.branchString + "\n\n" + "- Replace " + (data.overlayString1.Length / 3 + 1) + " bytes of data at overlay" + data.overlayNumber + " offset 0x" + data.overlayOffset1.ToString("X") + " with " + '\n' + data.overlayString1 + "\n\n" + "- Replace " + (data.overlayString2.Length / 3 + 1) + " bytes of data at overlay" + data.overlayNumber + " offset 0x" + data.overlayOffset2.ToString("X") + " with " + '\n' + data.overlayString2 + "\n\n" + "- Modify file #" + expandedARMfileID + " inside " + '\n' + RomInfo.gameDirs[DirNames.synthOverlay].unpackedDir + '\n' + "to insert the BDHCAM routine (any data between 0x" + ToolboxDB.BDHCamSubroutineOffset.ToString("X") + " and 0x" + (ToolboxDB.BDHCamSubroutineOffset + data.subroutine.Length).ToString("X") + " will be overwritten)." + "\n\n" + "Do you wish to continue?", "Confirm to proceed", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (d2 == DialogResult.Yes) { File.Copy(RomInfo.arm9Path, RomInfo.arm9Path + ".backup", overwrite: true); try { DSUtils.WriteToArm9(data.branchOffset, HexStringToByteArray(data.branchString)); //Write new branchOffset /* Write to overlayfile */ string overlayFilePath = RomInfo.workDir + "overlay" + "\\" + "overlay_" + data.overlayNumber.ToString("D4") + ".bin"; if (DSUtils.OverlayIsCompressed(data.overlayNumber)) DSUtils.DecompressOverlay(data.overlayNumber, true); DSUtils.WriteToFile(overlayFilePath, data.overlayOffset1, HexStringToByteArray(data.overlayString1)); //Write new overlayCode1 DSUtils.WriteToFile(overlayFilePath, data.overlayOffset2, HexStringToByteArray(data.overlayString2)); //Write new overlayCode2 overlay1MustBeRestoredFromBackup = false; String fullFilePath = RomInfo.gameDirs[DirNames.synthOverlay].unpackedDir + '\\' + expandedARMfileID.ToString("D4"); /*Write Expanded ARM9 File*/ DSUtils.WriteToFile(fullFilePath, ToolboxDB.BDHCamSubroutineOffset, data.subroutine); } catch { MessageBox.Show("Operation failed. It is strongly advised that you restore the arm9 and overlay from their respective backups.", "Something went wrong", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } overlay1MustBeRestoredFromBackup = false; DisableBDHCamPatch("Already applied"); flag_BDHCamPatchApplied = true; BDHCamCB.Visible = true; MessageBox.Show("The BDHCAM patch has been applied.", "Operation successful.", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("No changes have been made.", "Operation canceled", MessageBoxButtons.OK, MessageBoxIcon.Information); } } private void overlay1uncomprButton_Click(object sender, EventArgs e) { DialogResult d = MessageBox.Show("This process will apply the following changes:\n\n" + "- Overlay 1 will be decompressed.\n\n" + "- Overlay 1 will be configured as \"uncompressed\" in the overlay table.\n\n" + "Do you wish to continue?", "Confirm to proceed", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (d == DialogResult.Yes) { DSUtils.SetOverlayCompressionInTable(1, 0); if (DSUtils.OverlayIsCompressed(1)) DSUtils.DecompressOverlay(1, true); DisableOverlay1patch("Already applied"); overlay1CB.Visible = true; MessageBox.Show("Overlay1 is now configured as uncompressed.", "Operation successful", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("No changes have been made.", "Operation canceled", MessageBoxButtons.OK, MessageBoxIcon.Information); } } private void ApplyItemStandardizeButton_Click(object sender, EventArgs e) { DialogResult d = MessageBox.Show("This process will apply the following changes:\n\n" + "- Item scripts will be rearranged to follow the natural, ascending index order.\n\n" + "- Consequently, every Item event already on the ground will be changed.", "Confirm to proceed", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (d == DialogResult.Yes) { DSUtils.TryUnpackNarcs(new List { RomInfo.DirNames.scripts }); if (flag_standardizedItems) { AlreadyApplied(); } else { ScriptFile itemScript = new ScriptFile(RomInfo.itemScriptFileNumber); int itemCount = new TextArchive(RomInfo.itemNamesTextNumber).messages.Count; CommandContainer executeGive = new CommandContainer((uint)itemCount, itemScript.allScripts[itemScript.allScripts.Count - 1]); itemScript.allScripts.Clear(); for (ushort i = 0; i < itemCount; i++) { List cmdList = new List(); cmdList.Add(new ScriptCommand("SetVar 0x8008 " + i)); cmdList.Add(new ScriptCommand("SetVar 0x8009 0x1")); cmdList.Add(new ScriptCommand("Jump Function_#1")); itemScript.allScripts.Add(new CommandContainer((ushort)(i + 1), ScriptFile.containerTypes.SCRIPT, commandList: cmdList)); } itemScript.allScripts.Add(executeGive); //itemScript.allFunctions[1].commands[0].cmdParams[] itemScript.SaveToFileDefaultDir(RomInfo.itemScriptFileNumber, showSuccessMessage: false); MessageBox.Show("Operation successful.", "Process completed.", MessageBoxButtons.OK, MessageBoxIcon.Information); DisableStandardizeItemsPatch("Already applied"); itemNumbersCB.Visible = true; flag_standardizedItems = true; } } else { MessageBox.Show("No changes have been made.", "Operation canceled", MessageBoxButtons.OK, MessageBoxIcon.Information); } } private void ApplyARM9ExpansionButton_Click(object sender, EventArgs e) { ARM9PatchData data = new ARM9PatchData(); DialogResult d; d = MessageBox.Show("Confirming this process will apply the following changes:\n\n" + "- Backup ARM9 file (arm9.bin.backup will be created)." + "\n\n" + "- Replace " + (data.branchString.Length / 3 + 1) + " bytes of data at arm9 offset 0x" + data.branchOffset.ToString("X") + " with " + '\n' + data.branchString + "\n\n" + "- Replace " + (data.initString.Length / 3 + 1) + " bytes of data at arm9 offset 0x" + data.initOffset.ToString("X") + " with " + '\n' + data.initString + "\n\n" + "- Modify file #" + expandedARMfileID + " inside " + '\n' + RomInfo.gameDirs[DirNames.synthOverlay].unpackedDir + '\n' + " to accommodate for 88KB of data (no backup)." + "\n\n" + "Do you wish to continue?", "Confirm to proceed", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (d == DialogResult.Yes) { File.Copy(RomInfo.arm9Path, RomInfo.arm9Path + ".backup", overwrite: true); try { DSUtils.WriteToArm9(data.branchOffset, HexStringToByteArray(data.branchString)); //Write new branchOffset DSUtils.WriteToArm9(data.initOffset, HexStringToByteArray(data.initString)); //Write new initOffset string fullFilePath = RomInfo.gameDirs[DirNames.synthOverlay].unpackedDir + '\\' + expandedARMfileID.ToString("D4"); File.Delete(fullFilePath); using (BinaryWriter f = new BinaryWriter(File.Create(fullFilePath))) { for (int i = 0; i < 0x16000; i++) f.Write((byte)0x00); // Write Expanded ARM9 File } DisableARM9patch("Already applied"); arm9patchCB.Visible = true; flag_arm9Expanded = true; switch (RomInfo.gameFamily) { case "Plat": case "HGSS": BDHCamPatchButton.Text = "Apply Patch"; BDHCamPatchButton.Enabled = true; BDHCamPatchLBL.Enabled = true; BDHCamPatchTextLBL.Enabled = true; BDHCamARM9requiredLBL.Visible = false; break; } MessageBox.Show("The ARM9's usable memory has been expanded.", "Operation successful.", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch { MessageBox.Show("Operation failed. It is strongly advised that you restore the arm9 backup (arm9.bin.backup).", "Something went wrong", MessageBoxButtons.OK, MessageBoxIcon.Error); } } else { MessageBox.Show("No changes have been made.", "Operation canceled", MessageBoxButtons.OK, MessageBoxIcon.Information); } } private void expandMatrixButton_Click(object sender, EventArgs e) { string listOfChanges = ""; int languageOffset = 0; if (RomInfo.romID == "IPKE" || RomInfo.romID == "IPGE" || RomInfo.romID == "IPGS") languageOffset = +8; foreach (KeyValuePair kv in ToolboxDB.matrixExpansionDB) { listOfChanges += " - Replace " + (kv.Value.Length / 3 + 1) + " bytes of data at arm9 offset"; if (kv.Key.Length > 1) listOfChanges += "s"; for (int i = 0; i < kv.Key.Length; i++) { listOfChanges += " 0x" + (kv.Key[i] - 0x02000000 + languageOffset).ToString("X"); if (i < kv.Key.Length - 1) listOfChanges += ","; } listOfChanges += " with " + '\n' + kv.Value + "\n\n"; } DialogResult d; d = MessageBox.Show("Confirming this process will apply the following changes:\n\n" + listOfChanges + "Do you wish to continue?", "Confirm to proceed", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (d == DialogResult.Yes) { try { foreach (KeyValuePair kv in ToolboxDB.matrixExpansionDB) { foreach(uint offset in kv.Key) { DSUtils.WriteToArm9((uint)(offset - 0x02000000 + languageOffset), HexStringToByteArray(kv.Value)); } } } catch { MessageBox.Show("Operation failed. It is strongly advised that you restore the arm9 backup (arm9.bin.backup).", "Something went wrong", MessageBoxButtons.OK, MessageBoxIcon.Error); } DisableMatrixExpansionPatch("Already applied"); expandedMatrixCB.Visible = true; flag_MatrixExpansionApplied = true; MessageBox.Show("Matrix 0 can now be freely expanded up to twice its size.", "Operation successful.", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("No changes have been made.", "Operation canceled", MessageBoxButtons.OK, MessageBoxIcon.Information); } } #region Mikelan's custom commands private void applyCustomCommands(object sender, EventArgs e) { int expTableOffset = GetCommandTableOffset(); if (expTableOffset < 0) { DialogResult d; d = MessageBox.Show("Script command table has not been repointed.\n\n" + "Do you wish to repoint it to the expanded ARM9 file?\n\n" + "By default it will be written from 0x200 to 0x1700.\n" + "If you already have something there, you must cancel this window and move these things to a new location, or you can manually repoint the script command table to a different free location in the expanded ARM9 file", "Confirm to proceed", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (d == DialogResult.Yes) { RepointCommandTable(); } else { return; } } if (ImportCustomCommand()) { MessageBox.Show("Script commands succesfully installed in the ROM", "Done", MessageBoxButtons.OK, MessageBoxIcon.Information); } } private int GetCommandTableOffset() { // Checks if command table is repointed IN THE EXPANDED ARM9 FILE, returns pointer inside this file ResourceManager customcmdDB = new ResourceManager("DSPRE.Resources.ROMToolboxDB.CustomScrCmdDB", Assembly.GetExecutingAssembly()); int pointerOffset = int.Parse(customcmdDB.GetString("pointerOffset" + "_" + RomInfo.gameVersion + "_" + RomInfo.gameLanguage)); using (BinaryReader arm9Reader = new BinaryReader(new FileStream(RomInfo.arm9Path, FileMode.Open))) { arm9Reader.BaseStream.Position = pointerOffset; int cmdTable = arm9Reader.ReadInt32(); if (((cmdTable - 0x023C8000) >= 0) && ((cmdTable - 0x023C8000) <= 0x12B00)) { return (cmdTable - 0x023C8000); // Table position inside the expanded arm9 file } } return -1; // No table in expanded arm9 file } private void RepointCommandTable() { string expandedPath = RomInfo.gameDirs[DirNames.synthOverlay].unpackedDir + "\\0000"; ResourceManager customcmdDB = new ResourceManager("DSPRE.Resources.ROMToolboxDB.CustomScrCmdDB", Assembly.GetExecutingAssembly()); FileStream arm9FileStream = new FileStream(RomInfo.arm9Path, FileMode.Open); // I make a copy of the stream so the file is free for writing MemoryStream arm9Stream = new MemoryStream(); arm9FileStream.CopyTo(arm9Stream); byte[] cmdTbl = arm9Stream.ToArray(); using (BinaryWriter expArmWriter = new BinaryWriter(new FileStream(expandedPath, FileMode.Open))) { expArmWriter.BaseStream.Position = 0x200; // Command table default offset expArmWriter.Write(cmdTbl, int.Parse(customcmdDB.GetString("originalTableOffset" + "_" + RomInfo.gameVersion + "_" + RomInfo.gameLanguage)), 4 * 0x355); } arm9FileStream.Close(); using (BinaryWriter arm9Writer = new BinaryWriter(new FileStream(RomInfo.arm9Path, FileMode.Open))) // Change both the pointer and the limit { arm9Writer.BaseStream.Position = int.Parse(customcmdDB.GetString("pointerOffset" + "_" + RomInfo.gameVersion + "_" + RomInfo.gameLanguage)); arm9Writer.Write((uint)0x023C8200); arm9Writer.BaseStream.Position = int.Parse(customcmdDB.GetString("limitOffset" + "_" + RomInfo.gameVersion + "_" + RomInfo.gameLanguage)); arm9Writer.Write((uint)0x053C); } } private bool ImportCustomCommand() { string expandedPath = RomInfo.gameDirs[DirNames.synthOverlay].unpackedDir + "\\0000"; int appliedPatches = 0; OpenFileDialog of = new OpenFileDialog(); of.Filter = "Custom Script Command File (*.scrcmd)|*.scrcmd"; if (of.ShowDialog(this) != DialogResult.OK) return false; FileStream expandedFileStream = new FileStream(expandedPath, FileMode.Open); MemoryStream expandedStream = new MemoryStream(); expandedFileStream.CopyTo(expandedStream); expandedFileStream.Close(); using (BinaryWriter expandedWriter = new BinaryWriter(new FileStream(expandedPath, FileMode.Open))) { using (BinaryReader expandedReader = new BinaryReader(expandedStream)) { try { System.Xml.Linq.XDocument xmldoc = System.Xml.Linq.XDocument.Load(new FileStream(of.FileName, FileMode.Open)); foreach (var node in xmldoc.Root.Elements("scriptcommand")) { ushort commandID = UInt16.Parse(node.Attribute("ID").Value, System.Globalization.NumberStyles.HexNumber); string targetROM = node.Element("ROM").Value; string targetLang = node.Element("lang").Value; string commandName = node.Element("name").Value; string paramCount = node.Element("paramcount").Value; string paramCode = node.Element("paramcode").Value; int asmOffset = Int32.Parse(node.Element("asmoffset").Value, System.Globalization.NumberStyles.HexNumber); string asmCode = node.Element("asmcode").Value.Replace("\n", "").Replace("\t", "").Replace(" ", ""); if ((RomInfo.gameVersion != targetROM) || (RomInfo.gameLanguage != targetLang)) { continue; } expandedReader.BaseStream.Position = 0x200 + commandID * 4; if (expandedReader.ReadUInt32() != 0) { DialogResult d; d = MessageBox.Show("Script command " + commandID.ToString("X4") + " is already used.\n\n" + "Do you really want to overwrite it?", "Confirm to proceed", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (d == DialogResult.No) { continue; } } expandedWriter.BaseStream.Position = 0x200 + commandID * 4; expandedWriter.Write((UInt32)(0x023C8000 + asmOffset + 1)); byte[] asmCodeBytes = StringToByteArray(asmCode); expandedWriter.BaseStream.Position = asmOffset; expandedWriter.Write(asmCodeBytes); appliedPatches++; } } catch { MessageBox.Show("Selected command installation file is corrupted.\n\n" + "Please, download it again or contact its creator.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } } } if (appliedPatches == 0) { MessageBox.Show("No command could be installed from this file.\n\n" + "Make sure the command installation file supports your current ROM.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); return false; } return true; } #endregion #endregion #region Utilities //Ummm what? private byte[] StringToByteArray(String hex) { int NumberChars = hex.Length; byte[] bytes = new byte[NumberChars / 2]; for (int i = 0; i < NumberChars; i += 2) bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); return bytes; } public static byte[] HexStringToByteArray(string hexString) { //FC B5 05 48 C0 46 41 21 //09 22 02 4D A8 47 00 20 //03 21 FC BD F1 64 00 02 //00 80 3C 02 if (hexString == null) return null; hexString = hexString.Trim(); byte[] b = new byte[hexString.Length / 3 + 1]; for (int i = 0; i < hexString.Length; i += 2) { if (hexString[i] == ' ') { hexString = hexString.Substring(1, hexString.Length - 1); } b[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16); } return b; } #endregion #region Error Messsages private void UnsupportedROM() { MessageBox.Show("This operation is currently impossible to carry out on any Pokémon " + RomInfo.gameName + " rom.", "Unsupported ROM", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void UnsupportedROMLanguage() { MessageBox.Show("This operation is currently impossible to carry out on the " + RomInfo.gameLanguage + " version of this rom.", "Unsupported Language", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void AlreadyApplied() { MessageBox.Show("This patch has already been applied.", "Can't reapply patch", MessageBoxButtons.OK, MessageBoxIcon.Information); } #endregion private void repointScrcmdButton_Click(object sender, EventArgs e) { } } }