using DSPRE.Editors; using DSPRE.Editors.BtxEditor; using DSPRE.Resources; using DSPRE.ROMFiles; using Microsoft.WindowsAPICodePack.Dialogs; using NarcAPI; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; using static DSPRE.EditorPanels; using static DSPRE.Helpers; using static DSPRE.RomInfo; namespace DSPRE { public partial class MainProgram : Form { public MainProgram() { #if DEBUG AppLogger.Initialize(this, minLevel: LogLevel.Debug); #else AppLogger.Initialize(this, minLevel: LogLevel.Info); #endif AppLogger.Info("=== Application started. === "); SettingsManager.Load(); // Updates can't be checked if the application is not installed, hence the !debug #if !DEBUG if(SettingsManager.Settings.automaticallyCheckForUpdates) { try { Helpers.CheckForUpdates(); } catch { AppLogger.Error("Failed to check for updates."); MessageBox.Show("Failed to check for updates.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } #endif InitializeComponent(); Program.SetupDatabase(); EditorPanels.Initialize(this); Helpers.Initialize(this); WireEditorsPopout(); SetMenuLayout(SettingsManager.Settings.menuLayout); //Read user settings for menu layout Text = "DS Pokémon Rom Editor Reloaded " + GetDSPREVersion() + " (Nømura, AdAstra/LD3005, Mixone)"; string romFolder = SettingsManager.Settings.openDefaultRom; if (romFolder != string.Empty) { AppLogger.Info($"Detected stored ROM folder: {romFolder}"); if (!SettingsManager.Settings.neverAskForOpening) { AppLogger.Debug("Prompting user to confirm auto-opening the ROM folder."); ReopenProjectConfirmation confirmOpen = new ReopenProjectConfirmation(); if (confirmOpen.ShowDialog() == DialogResult.No) { AppLogger.Info("User declined to reopen the previous ROM project."); return; } AppLogger.Info("User confirmed reopening the previous ROM project."); } else { AppLogger.Info("Auto-opening ROM without asking the user (neverAskForOpening is enabled)."); } AppLogger.Info("Opening ROM project from saved folder."); OpenRomFromFolder(romFolder); } else { AppLogger.Debug("No stored ROM folder found on startup."); } } #region Program Window #region Variables public bool iconON = false; /* ROM Information */ public static string gameCode; public static byte revisionByte; public RomInfo romInfo; public Dictionary eventToHeader = new Dictionary(); #endregion #region Subroutines private void SetMenuLayout(byte layoutStyle) { AppLogger.Debug("Setting menuLayout to" + layoutStyle); IList list = menuViewToolStripMenuItem.DropDownItems; for (int i = 0; i < list.Count; i++) { (list[i] as ToolStripMenuItem).Checked = (i == layoutStyle); } SettingsManager.Settings.menuLayout = layoutStyle; switch (layoutStyle) { case 0: buildNarcFromFolderToolStripButton.Visible = false; unpackNARCtoFolderToolStripButton.Visible = false; separator_afterNarcUtils.Visible = false; listBasedBatchRenameToolStripButton.Visible = false; contentBasedBatchRenameToolStripButton.Visible = false; separator_afterRenameUtils.Visible = false; enumBasedListBuilderToolStripButton.Visible = false; folderBasedListBuilderToolStriButton.Visible = false; separator_afterListUtils.Visible = false; nsbmdAddTexButton.Visible = false; nsbmdRemoveTexButton.Visible = false; nsbmdExportTexButton.Visible = false; separator_afterNsbmdUtils.Visible = false; wildEditorButton.Visible = false; romToolboxToolStripButton.Visible = false; break; case 1: buildNarcFromFolderToolStripButton.Visible = false; unpackNARCtoFolderToolStripButton.Visible = false; separator_afterNarcUtils.Visible = false; listBasedBatchRenameToolStripButton.Visible = false; contentBasedBatchRenameToolStripButton.Visible = false; separator_afterRenameUtils.Visible = false; enumBasedListBuilderToolStripButton.Visible = false; folderBasedListBuilderToolStriButton.Visible = false; separator_afterListUtils.Visible = false; nsbmdAddTexButton.Visible = true; nsbmdRemoveTexButton.Visible = true; nsbmdExportTexButton.Visible = true; separator_afterNsbmdUtils.Visible = true; wildEditorButton.Visible = true; romToolboxToolStripButton.Visible = true; break; case 2: buildNarcFromFolderToolStripButton.Visible = true; unpackNARCtoFolderToolStripButton.Visible = true; separator_afterNarcUtils.Visible = true; listBasedBatchRenameToolStripButton.Visible = false; contentBasedBatchRenameToolStripButton.Visible = false; separator_afterRenameUtils.Visible = false; enumBasedListBuilderToolStripButton.Visible = false; folderBasedListBuilderToolStriButton.Visible = false; separator_afterListUtils.Visible = false; nsbmdAddTexButton.Visible = true; nsbmdRemoveTexButton.Visible = true; nsbmdExportTexButton.Visible = true; separator_afterNsbmdUtils.Visible = true; wildEditorButton.Visible = true; romToolboxToolStripButton.Visible = true; break; case 3: default: foreach (ToolStripItem c in mainToolStrip.Items) { c.Visible = true; } break; } } private void MainProgram_FormClosing(object sender, FormClosingEventArgs e) { if (e.CloseReason != CloseReason.ApplicationExitCall && MessageBox.Show("Are you sure you want to quit?", "Exit", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { e.Cancel = true; } SettingsManager.Save(); } private void MainProgram_Shown(object sender, EventArgs e) { if (!DetectRequiredTools()) { BeginInvoke(new Action(() => Application.Exit())); return; } } private void PaintGameIcon(object sender, PaintEventArgs e) { if (iconON) { FileStream banner; try { banner = File.OpenRead(RomInfo.workDir + @"banner.bin"); } catch (FileNotFoundException) { MessageBox.Show("Couldn't load " + '"' + "banner.bin" + '"' + '.', "Open Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } BinaryReader readIcon = new BinaryReader(banner); #region Read Icon Palette readIcon.BaseStream.Position = 0x220; byte firstByte, secondByte; int palR, palG, palB; int palCounter = 0; int[] paletteArray = new int[48]; for (int i = 0; i < 16; i++) { palR = 0; palG = 0; palB = 0; secondByte = readIcon.ReadByte(); firstByte = readIcon.ReadByte(); if ((firstByte & (1 << 6)) != 0) palB |= (1 << 4); if ((firstByte & (1 << 5)) != 0) palB |= (1 << 3); if ((firstByte & (1 << 4)) != 0) palB |= (1 << 2); if ((firstByte & (1 << 3)) != 0) palB |= (1 << 1); if ((firstByte & (1 << 2)) != 0) palB |= (1 << 0); if ((firstByte & (1 << 1)) != 0) palG |= (1 << 4); if ((firstByte & (1 << 0)) != 0) palG |= (1 << 3); if ((secondByte & (1 << 7)) != 0) palG |= (1 << 2); if ((secondByte & (1 << 6)) != 0) palG |= (1 << 1); if ((secondByte & (1 << 5)) != 0) palG |= (1 << 0); if ((secondByte & (1 << 4)) != 0) palR |= (1 << 4); if ((secondByte & (1 << 3)) != 0) palR |= (1 << 3); if ((secondByte & (1 << 2)) != 0) palR |= (1 << 2); if ((secondByte & (1 << 1)) != 0) palR |= (1 << 1); if ((secondByte & (1 << 0)) != 0) palR |= (1 << 0); paletteArray[palCounter++] = palR * 8; paletteArray[palCounter++] = palG * 8; paletteArray[palCounter++] = palB * 8; } #endregion #region Read Icon Image readIcon.BaseStream.Position = 0x20; int iconY = 0; int xTile = 0; int yTile = 0; for (int o = 0; o < 4; o++) { for (int a = 0; a < 4; a++) { for (int i = 0; i < 8; i++) { int iconX = xTile; for (int counter = 0; counter < 4; counter++) { byte pixelByte = readIcon.ReadByte(); int pixelPalId = pixelByte & 0x0F; Brush icon = new SolidBrush(Color.FromArgb(255, paletteArray[pixelPalId * 3], paletteArray[pixelPalId * 3 + 1], paletteArray[pixelPalId * 3 + 2])); e.Graphics.FillRectangle(icon, iconX, i + yTile, 1, 1); iconX++; pixelPalId = (pixelByte & 0xF0) >> 4; icon = new SolidBrush(Color.FromArgb(255, paletteArray[pixelPalId * 3], paletteArray[pixelPalId * 3 + 1], paletteArray[pixelPalId * 3 + 2])); e.Graphics.FillRectangle(icon, iconX, i + yTile, 1, 1); iconX++; } iconY++; } iconY = 0; xTile += 8; } xTile = 0; yTile += 8; } #endregion readIcon.Close(); } } public void SetupScriptEditor() { /* Extract essential NARCs sub-archives*/ Helpers.statusLabelMessage("Setting up Script Editor..."); Update(); DSUtils.TryUnpackNarcs(new List { DirNames.scripts }); //12 = scripts Narc Dir EditorPanels.scriptEditor.selectScriptFileComboBox.Items.Clear(); int scriptCount = Directory.GetFiles(RomInfo.gameDirs[DirNames.scripts].unpackedDir).Length; for (int i = 0; i < scriptCount; i++) { EditorPanels.scriptEditor.selectScriptFileComboBox.Items.Add("Script File " + i); } EditorPanels.scriptEditor.UpdateScriptNumberCheckBox((NumberStyles)SettingsManager.Settings.scriptEditorFormatPreference); EditorPanels.scriptEditor.selectScriptFileComboBox.SelectedIndex = 0; Helpers.statusLabelMessage(); } /// /// Check if extracted data for the ROM exists, and ask user if they want to load it. /// /// /// -1 - Do nothing, no data found /// 0 - User wants to abort loading /// 1 - User wants to load existing data /// 2 - User wants to re-extract data /// private int UnpackRomCheckUserChoice(string romDir) { switch (DSUtils.GetFolderType(romDir)) { case -1: return -1; // Do nothing case case 0: MessageBox.Show("Extracted data of this ROM is not yet supported.\n" + "Loading will be aborted.", "Unsupported ROM", MessageBoxButtons.OK, MessageBoxIcon.Error); return 0; //user wants to abort loading case 1: DialogResult d2 = MessageBox.Show("Extracted data of this ROM has been found.\n" + "Do you want to load it?", "Extracted data detected", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); if (d2 == DialogResult.Cancel) { return 0; //user wants to abort loading } else if (d2 == DialogResult.Yes) { return 1; //user wants to load data } DialogResult nd2 = MessageBox.Show("All data of this ROM will be re-extracted. Proceed?\n", "Existing data will be deleted", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (nd2 == DialogResult.Yes) { return 2; //user wants to re-extract data } return 0; //user wants to abort loading default: return -1; // Do nothing case } } #endregion private void romToolBoxToolStripMenuItem_Click(object sender, EventArgs e) { using (PatchToolboxDialog window = new PatchToolboxDialog()) { window.ShowDialog(); if (PatchToolboxDialog.flag_standardizedItems && EditorPanels.eventEditor.eventEditorIsReady) { EditorPanels.eventEditor.selectEventComboBox_SelectedIndexChanged(null, null); UpdateItemComboBox(RomInfo.GetItemNames()); } if (PatchToolboxDialog.flag_DynamicHeadersPatchApplied) { EditorPanels.headerEditor.addHeaderBTN.Enabled = true; EditorPanels.headerEditor.removeLastHeaderBTN.Enabled = true; } } } public void UpdateItemComboBox(string[] itemNames) { if (EditorPanels.eventEditor.itemComboboxIsUpToDate) { return; } EditorPanels.eventEditor.itemsSelectorHelpBtn.Visible = false; EditorPanels.eventEditor.owItemComboBox.Size = new Size(new Point(EditorPanels.eventEditor.owItemComboBox.Size.Width + 30, EditorPanels.eventEditor.owItemComboBox.Size.Height)); EditorPanels.eventEditor.owItemComboBox.Items.Clear(); EditorPanels.eventEditor.owItemComboBox.Items.AddRange(itemNames); EditorPanels.eventEditor.OWTypeChanged(null, null); EditorPanels.eventEditor.itemComboboxIsUpToDate = true; } private void scriptCommandsDatabaseToolStripButton_Click(object sender, EventArgs e) { OpenCommandsDatabase(RomInfo.ScriptCommandNamesDict, RomInfo.ScriptCommandParametersDict, RomInfo.ScriptActionNamesDict, RomInfo.ScriptComparisonOperatorsDict); } private void nsbmdExportTexButton_Click(object sender, EventArgs e) { OpenFileDialog of = new OpenFileDialog { Filter = MapFile.TexturedNSBMDFilter }; if (of.ShowDialog(this) != DialogResult.OK) { return; } byte[] modelFile = DSUtils.ReadFromFile(of.FileName); if (NSBUtils.CheckNSBMDHeader(modelFile) == NSBUtils.NSBMD_DOESNTHAVE_TEXTURE) { MessageBox.Show("This NSBMD file is untextured.", "No textures to extract", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } //============================================================ MessageBox.Show("Choose where to save the textures.", "Choose destination path", MessageBoxButtons.OK, MessageBoxIcon.Information); SaveFileDialog texSf = new SaveFileDialog { Filter = "NSBTX File(*.nsbtx)|*.nsbtx", FileName = Path.GetFileNameWithoutExtension(of.FileName) }; if (texSf.ShowDialog() != DialogResult.OK) { return; } DSUtils.WriteToFile(texSf.FileName, NSBUtils.GetTexturesFromTexturedNSBMD(modelFile)); MessageBox.Show("The textures of " + of.FileName + " have been extracted and saved.", "Textures saved", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void nsbmdRemoveTexButton_Click(object sender, EventArgs e) { OpenFileDialog of = new OpenFileDialog { Filter = MapFile.TexturedNSBMDFilter }; if (of.ShowDialog(this) != DialogResult.OK) { return; } byte[] modelFile = DSUtils.ReadFromFile(of.FileName); if (NSBUtils.CheckNSBMDHeader(modelFile) == NSBUtils.NSBMD_DOESNTHAVE_TEXTURE) { MessageBox.Show("This NSBMD file is already untextured.", "No textures to remove", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } string extramsg = ""; DialogResult d = MessageBox.Show("Would you like to save the removed textures to a file?", "Save textures?", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (d.Equals(DialogResult.Yes)) { MessageBox.Show("Choose where to save the textures.", "Choose destination path", MessageBoxButtons.OK, MessageBoxIcon.Information); SaveFileDialog texSf = new SaveFileDialog { Filter = "NSBTX File(*.nsbtx)|*.nsbtx", FileName = Path.GetFileNameWithoutExtension(of.FileName) }; if (texSf.ShowDialog() == DialogResult.OK) { DSUtils.WriteToFile(texSf.FileName, NSBUtils.GetTexturesFromTexturedNSBMD(modelFile)); extramsg = " exported and"; } } //============================================================ MessageBox.Show("Choose where to save the untextured model.", "Choose destination path", MessageBoxButtons.OK, MessageBoxIcon.Information); SaveFileDialog sf = new SaveFileDialog { Filter = "Untextured NSBMD File(*.nsbmd)|*.nsbmd", FileName = Path.GetFileNameWithoutExtension(of.FileName) + "_untextured" }; if (sf.ShowDialog(this) != DialogResult.OK) { return; } DSUtils.WriteToFile(sf.FileName, NSBUtils.GetModelWithoutTextures(modelFile)); MessageBox.Show("Textures correctly" + extramsg + " removed!", "Success!", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void nsbmdAddTexButton_Click(object sender, EventArgs e) { OpenFileDialog of = new OpenFileDialog { Filter = MapFile.UntexturedNSBMDFilter }; if (of.ShowDialog(this) != DialogResult.OK) return; byte[] modelFile = File.ReadAllBytes(of.FileName); if (NSBUtils.CheckNSBMDHeader(modelFile) == NSBUtils.NSBMD_HAS_TEXTURE) { DialogResult d = MessageBox.Show("This NSBMD file is already textured.\nDo you want to overwrite its textures?", "Textures found", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (d.Equals(DialogResult.No)) { return; } } MessageBox.Show("Select the new NSBTX texture file.", "Choose NSBTX", MessageBoxButtons.OK, MessageBoxIcon.Information); OpenFileDialog openNsbtx = new OpenFileDialog { Filter = "NSBTX File(*.nsbtx)|*.nsbtx" }; if (openNsbtx.ShowDialog(this) != DialogResult.OK) { return; } byte[] textureFile = File.ReadAllBytes(openNsbtx.FileName); //============================================================ MessageBox.Show("Choose where to save the new textured model.", "Choose destination path", MessageBoxButtons.OK, MessageBoxIcon.Information); string texturedPath = Path.GetFileNameWithoutExtension(of.FileName); if (texturedPath.Contains("_untextured")) { texturedPath = texturedPath.Substring(0, texturedPath.Length - "_untextured".Length); } SaveFileDialog sf = new SaveFileDialog { Filter = MapFile.TexturedNSBMDFilter, FileName = Path.GetFileNameWithoutExtension(of.FileName) + "_textured" }; if (sf.ShowDialog(this) != DialogResult.OK) return; DSUtils.WriteToFile(sf.FileName, NSBUtils.BuildNSBMDwithTextures(modelFile, textureFile), fmode: FileMode.Create); MessageBox.Show("Textures correctly written to NSBMD file.", "Success!", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void OpenCommandsDatabase(Dictionary namesDict, Dictionary paramsDict, Dictionary actionsDict, Dictionary comparisonOPsDict) { Helpers.statusLabelMessage("Setting up Commands Database. Please wait..."); Update(); CommandsDatabase form = new CommandsDatabase(namesDict, paramsDict, actionsDict, comparisonOPsDict); form.Show(); Helpers.statusLabelMessage(); } private void headerSearchToolStripButton_Click(object sender, EventArgs e) { mainTabControl.SelectedIndex = 0; //Select Header Editor using (HeaderSearch h = new HeaderSearch(ref EditorPanels.headerEditor.internalNames, EditorPanels.headerEditor.headerListBox, statusLabel)) { h.ShowDialog(); } } private void advancedHeaderSearchToolStripMenuItem_Click(object sender, EventArgs e) { headerSearchToolStripButton_Click(null, null); } private void buildingEditorButton_Click(object sender, EventArgs e) { unpackBuildingEditorNARCs(); BuildingEditor editor = new BuildingEditor(romInfo); editor.Show(); } private void unpackBuildingEditorNARCs(bool forceUnpack = false) { toolStripProgressBar.Visible = true; Helpers.statusLabelMessage("Attempting to unpack Building Editor NARCs... Please wait. This might take a while"); toolStripProgressBar.Visible = true; toolStripProgressBar.Maximum = 4; toolStripProgressBar.Value = 0; Update(); List toUnpack = new List { DirNames.exteriorBuildingModels, DirNames.buildingConfigFiles, DirNames.buildingTextures, DirNames.areaData }; if (forceUnpack) { DSUtils.ForceUnpackNarcs(toUnpack); if (RomInfo.gameFamily == GameFamilies.HGSS) { DSUtils.ForceUnpackNarcs(new List { DirNames.interiorBuildingModels });// Last = interior buildings dir } } else { DSUtils.TryUnpackNarcs(toUnpack); if (RomInfo.gameFamily == GameFamilies.HGSS) { DSUtils.TryUnpackNarcs(new List { DirNames.interiorBuildingModels }); } } toolStripProgressBar.Value = 0; toolStripProgressBar.Visible = false; Helpers.statusLabelMessage(); Update(); } private void aboutToolStripMenuItem_Click(object sender, EventArgs e) { string message = "DS Pokémon ROM Editor Reloaded by AdAstra/LD3005" + Environment.NewLine + "version " + GetDSPREVersion() + Environment.NewLine + Environment.NewLine + "Based on Nømura's DS Pokémon ROM Editor 1.0.4." + Environment.NewLine + "Largely inspired by Markitus95's \"Spiky's DS Map Editor\" (SDSME), from which certain assets were also reused." + "Credits go to Markitus, Ark, Zark, Florian, and everyone else who deserves credit for SDSME." + Environment.NewLine + Environment.NewLine + "Special thanks to Trifindo, Mikelan98, Mixone, JackHack96, Pleonex and BagBoy." + Environment.NewLine + "Their help, research and expertise in many fields of NDS ROM Hacking made the development of this tool possible."; MessageBox.Show(message, "About...", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void loadRom_Click(object sender, EventArgs e) { OpenFileDialog openRom = new OpenFileDialog { Filter = DSUtils.NDSRomFilter }; // Select ROM if (openRom.ShowDialog(this) != DialogResult.OK) { AppLogger.Debug("User cancelled the ROM loading dialog."); return; } // Validate path and check for OneDrive if (!ValidateFilePath(openRom.FileName)) { AppLogger.Warn("ROM path validation failed. Loading Aborted!"); return; } string workDir = DSUtils.WorkDirPathFromFile(openRom.FileName); AppLogger.Info(workDir + " will be used as the working directory for the ROM."); int userchoice = UnpackRomCheckUserChoice(workDir); switch (userchoice) { case -1: if (!DSUtils.UnpackRom(openRom.FileName, workDir)) { AppLogger.Error($"Unpacking of ROM \"{openRom.FileName}\" has failed!"); Helpers.statusLabelError($"Unpacking of ROM ROM \"{openRom.FileName}\" has failed"); Update(); return; // Unpacking failed, abort loading } break; case 0: AppLogger.Info("User chose to abort loading the ROM."); Helpers.statusLabelMessage("Loading aborted"); Update(); return; case 1: AppLogger.Info("User chose to load existing data from " + workDir); Application.DoEvents(); break; case 2: AppLogger.Info("User chose to re-extract data from " + openRom.FileName); Application.DoEvents(); Helpers.statusLabelMessage("Deleting old data..."); Update(); try { Directory.Delete(workDir, true); AppLogger.Debug(workDir + " was deleted successfully."); } catch (IOException) { AppLogger.Error("Concurrent access detected while trying to delete " + workDir); MessageBox.Show("Concurrent access detected: \n" + workDir + "\nMake sure no other process is using the extracted ROM folder while DSPRE is running.", "Concurrent Access", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (!DSUtils.UnpackRom(openRom.FileName, workDir)) { AppLogger.Error($"Unpacking of ROM \"{openRom.FileName}\" has failed!"); Helpers.statusLabelError("Unpacking of ROM \"" + openRom.FileName + "\" has failed"); Update(); return; // Unpacking failed, abort loading } break; } AppLogger.Info("ROM unpacked successfully, proceeding to open the ROM from " + workDir); Update(); OpenRomFromFolder(workDir); } private bool ValidateFilePath(string fileName) { // Empty file name check if (string.IsNullOrWhiteSpace(fileName)) { MessageBox.Show("File path is empty.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } string fullPath = Path.GetFullPath(fileName); // File / directory existence check if (!File.Exists(fileName) && !Directory.Exists(fileName)) { MessageBox.Show("The specified file at path " + fullPath + " does not exist.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } // One drive check if (fullPath.ToLower().Contains("onedrive")) { MessageBox.Show("OneDrive was detected in the path. DSPRE is not compatible with OneDrive. " + "Please move the ROM and unpacked folder to the same local drive DSPRE is stored on.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } return true; } private bool DetectAndHandleWSL(string fileName) { string fullPath = Path.GetFullPath(fileName); if (!fullPath.ToLower().Contains("wsl.")) { return false; // No WSL detected, proceed normally } MessageBox.Show("WSL was detected in the path. " + "You may experience some slow-downs, especially on WSL2.\n" + "If the slow-down is too excessive consider moving your files to the Windows filesystem.", "WSL Detected", MessageBoxButtons.OK, MessageBoxIcon.Information); AppLogger.Info("WSL detected in the path: " + fullPath); return true; } private bool DetectRequiredTools() { bool toolsMissing = false; List missingToolsList = new List(); if (!File.Exists(@"Tools\ndstool.exe")) { toolsMissing = true; missingToolsList.Add("ndstool.exe"); } if (!File.Exists(@"Tools\blz.exe")) { toolsMissing = true; missingToolsList.Add("blz.exe"); } if (!File.Exists(@"Tools\apicula.exe")) { toolsMissing = true; missingToolsList.Add("apicula.exe"); } if (toolsMissing) { string message = "The following required tools are missing from the DSPRE Tools folder:\n-" + string.Join("\n-", missingToolsList) + "\n\n" + "Please ensure that the Tools folder is intact and contains all necessary files.\n" + "Common causes for this issue are:\n" + " - DSPRE is stored in OneDrive\n" + " - You opened DSPRE from Windows search\n" + " - Your Antivirus software has removed critical files\n\n" + "DSPRE will now close."; MessageBox.Show(message, "Missing Tools", MessageBoxButtons.OK, MessageBoxIcon.Error); // If the program somehow doesn't close after this, we also disable the buttons and hope this is enough to dissuade the user from using it. this.loadRomButton.Enabled = false; // Disable Load ROM button this.readDataFromFolderButton.Enabled = false; // Disable Read Data from Folder button this.fileToolStripMenuItem.DropDownItems[0].Enabled = false; // Disable Open ROM menu item this.fileToolStripMenuItem.DropDownItems[1].Enabled = false; // Disable Open Folder menu item return false; } return true; } private void CheckROMLanguage() { versionLabel.Visible = true; languageLabel.Visible = true; versionLabel.Text = RomInfo.gameVersion.ToString() + " " + "[" + RomInfo.romID + "]"; languageLabel.Text = "Lang: " + RomInfo.gameLanguage; if (RomInfo.gameLanguage == GameLanguages.English) { if (revisionByte == 0x0A) { languageLabel.Text += " [Europe]"; } else { languageLabel.Text += " [America]"; } } } private void readDataFromFolderButton_Click(object sender, EventArgs e) { CommonOpenFileDialog romFolder = new CommonOpenFileDialog { IsFolderPicker = true, Multiselect = false }; if (romFolder.ShowDialog() != CommonFileDialogResult.Ok) { return; } string fileName = romFolder.FileName; OpenRomFromFolder(fileName); } private void OpenRomFromFolder(string romFolderPath) { AppLogger.Info($"Attempting to open ROM from folder: {romFolderPath}"); // Validate path and check for OneDrive if (!ValidateFilePath(romFolderPath)) { AppLogger.Warn("ROM path validation failed. Possibly invalid or on a restricted (OneDrive)."); return; } DetectAndHandleWSL(romFolderPath); if (DSUtils.GetFolderType(romFolderPath) == -1) { AppLogger.Error("The selected folder does not contain a valid ROM folder structure."); MessageBox.Show("The selected folder does not contain a valid ROM folder structure.", "Invalid Folder", MessageBoxButtons.OK, MessageBoxIcon.Error); return; // Invalid folder, abort loading } SetupROMLanguage(Path.Combine(romFolderPath, "header.bin")); AppLogger.Debug("ROM language setup completed."); romInfo = new RomInfo(gameCode, romFolderPath); if (string.IsNullOrWhiteSpace(RomInfo.romID) || string.IsNullOrWhiteSpace(RomInfo.projectName)) { AppLogger.Error("ROM ID or filename is empty after initialization. Aborting."); return; } AppLogger.Info($"ROM loaded successfully: ID = {RomInfo.romID}, Project Name = {RomInfo.projectName}"); CheckROMLanguage(); AppLogger.Debug("ROM language checked and applied."); iconON = true; gameIcon.Refresh(); // Paint game icon AppLogger.Debug("Game icon refreshed."); if (!CheckAndDecompressARM9()) { AppLogger.Error("ARM9 decompression failed. Aborting."); return; } ReadROMInitData(); AppLogger.Info("ROM initialization data loaded."); } private void SetupROMLanguage(string headerPath) { using (DSUtils.EasyReader br = new DSUtils.EasyReader(headerPath, 0xC)) { gameCode = Encoding.UTF8.GetString(br.ReadBytes(4)); br.BaseStream.Position = 0x1E; revisionByte = br.ReadByte(); } } private bool CheckAndDecompressARM9() { if (!ARM9.CheckCompressionMark()) { return true; // ARM9 is not compressed, proceed normally } if (!RomInfo.gameFamily.Equals(GameFamilies.HGSS)) { MessageBox.Show("Unexpected compressed ARM9. It is advised that you double check the ARM9."); return false; } ARM9.EditSize(-12); // Fix ARM9 size before decompression if (!ARM9.Decompress(RomInfo.arm9Path)) { MessageBox.Show("ARM9 decompression failed. The program can't proceed.\nAborting.", "Error with ARM9 decompression", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } AppLogger.Info("ARM9 decompressed and size fixed."); return true; } private void ReadROMInitData() { /* Setup essential editors */ EditorPanels.headerEditor.SetupHeaderEditor(this); mainTabControl.Show(); loadRomButton.Enabled = false; readDataFromFolderButton.Enabled = false; saveRomButton.Enabled = true; saveROMToolStripMenuItem.Enabled = true; openROMToolStripMenuItem.Enabled = false; openFolderToolStripMenuItem.Enabled = false; unpackAllButton.Enabled = true; updateMapNarcsButton.Enabled = true; buildingEditorButton.Enabled = true; wildEditorButton.Enabled = true; romToolboxToolStripButton.Enabled = true; romToolboxToolStripMenuItem.Enabled = true; headerSearchToolStripButton.Enabled = true; headerSearchToolStripMenuItem.Enabled = true; spawnEditorToolStripMenuItem.Enabled = true; otherEditorsToolStripMenuItem.Enabled = true; scriptCommandsButton.Enabled = true; if (!RomInfo.gameFamily.Equals(GameFamilies.HGSS)) { mainTabControl.TabPages.Remove(tabPageEncountersEditor); } else { overlayEditorToolStripMenuItem.Enabled = true; } Helpers.statusLabelMessage(); this.Text += " - " + RomInfo.projectName; } private void saveRom_Click(object sender, EventArgs e) { AppLogger.Info("Saving ROM..."); SaveFileDialog saveRom = new SaveFileDialog { Filter = DSUtils.NDSRomFilter, InitialDirectory = SettingsManager.Settings.exportPath }; if (saveRom.ShowDialog(this) != DialogResult.OK) { AppLogger.Debug("User cancelled the Save ROM dialog."); return; } Helpers.statusLabelMessage("Repacking NARCS..."); Update(); var dateBegin = DateTime.Now; // Repack NARCs foreach (KeyValuePair kvp in RomInfo.gameDirs) { DirectoryInfo di = new DirectoryInfo(kvp.Value.unpackedDir); if (di.Exists) { Narc.FromFolder(kvp.Value.unpackedDir).Save(kvp.Value.packedDir); // Make new NARC from folder } } if (ARM9.CheckCompressionMark()) { Helpers.statusLabelMessage("Awaiting user response..."); DialogResult d = MessageBox.Show("The ARM9 file of this ROM is currently uncompressed, but marked as compressed.\n" + "This will prevent your ROM from working on native hardware.\n\n" + "Do you want to mark the ARM9 as uncompressed?", "ARM9 compression mismatch detected", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (d == DialogResult.Yes) { ARM9.WriteBytes(new byte[4] { 0, 0, 0, 0 }, (uint)(RomInfo.gameFamily == GameFamilies.DP ? 0xB7C : 0xBB4)); } } Helpers.statusLabelMessage("Repacking ROM..."); if (OverlayUtils.OverlayTable.IsDefaultCompressed(1)) { if (PatchToolboxDialog.overlay1MustBeRestoredFromBackup) { OverlayUtils.RestoreFromCompressedBackup(1, EditorPanels.eventEditor.eventEditorIsReady); } else { if (!OverlayUtils.IsCompressed(1)) { OverlayUtils.Compress(1); } } } if (OverlayUtils.OverlayTable.IsDefaultCompressed(RomInfo.initialMoneyOverlayNumber)) { if (!OverlayUtils.IsCompressed(RomInfo.initialMoneyOverlayNumber)) { OverlayUtils.Compress(RomInfo.initialMoneyOverlayNumber); } } Update(); //for (int i = 0; i < 128; i++) { // if (!OverlayUtils.IsCompressed(i)) { // OverlayUtils.Compress(i); // } //} bool success = DSUtils.RepackROM(saveRom.FileName); if (RomInfo.gameFamily != GameFamilies.DP && RomInfo.gameFamily != GameFamilies.Plat) { if (EditorPanels.eventEditor.eventEditorIsReady) { if (OverlayUtils.IsCompressed(1)) { OverlayUtils.Decompress(1); } } } SettingsManager.Save(); Helpers.statusLabelMessage(); var date = DateTime.Now; var StringDate = Helpers.formatTime(date.Hour) + ":" + Helpers.formatTime(date.Minute) + ":" + Helpers.formatTime(date.Second); int timeSpent = Helpers.CalculateTimeDifferenceInSeconds(dateBegin.Hour, dateBegin.Minute, dateBegin.Second, date.Hour, date.Minute, date.Second); if (!success) { AppLogger.Error("An error occurred while repacking the ROM. Save failed."); Helpers.statusLabelError("An error occurred while repacking the ROM. Save failed. Your ROM may have been corrupted."); return; } AppLogger.Info($"ROM saved successfully to {saveRom.FileName} in {timeSpent} seconds."); Helpers.statusLabelMessage("Ready - " + StringDate + " | Build time: " + timeSpent.ToString() + "s | " + saveRom.FileName); } private void unpackAllButton_Click(object sender, EventArgs e) { Helpers.statusLabelMessage("Awaiting user response..."); DialogResult d = MessageBox.Show("Do you wish to unpack all extracted NARCS?\n" + "This operation might be long and can't be interrupted.\n\n" + "Any unsaved changes made to the ROM in this session will be lost." + "\nProceed?", "About to unpack all NARCS", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (d == DialogResult.Yes) { toolStripProgressBar.Maximum = RomInfo.gameDirs.Count; toolStripProgressBar.Visible = true; toolStripProgressBar.Value = 0; Helpers.statusLabelMessage("Attempting to unpack all NARCs... Be patient. This might take a while..."); Update(); DSUtils.ForceUnpackNarcs(Enum.GetValues(typeof(DirNames)).Cast().ToList()); MessageBox.Show("Operation completed.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); toolStripProgressBar.Value = 0; toolStripProgressBar.Visible = false; EditorPanels.headerEditor.SetupHeaderEditor(this); EditorPanels.matrixEditor.SetupMatrixEditor(this); EditorPanels.mapEditor.SetupMapEditor(this); nsbtxEditor.SetupNSBTXEditor(this); EditorPanels.eventEditor.SetupEventEditor(this); SetupScriptEditor(); textEditor.SetupTextEditor(this); trainerEditor.SetupTrainerEditor(this); Helpers.statusLabelMessage(); Update(); } } private void updateMapNarcsButton_Click(object sender, EventArgs e) { Helpers.statusLabelMessage("Awaiting user response..."); DialogResult d = MessageBox.Show("Do you wish to unpack all NARC files necessary for the Building Editor ?\n" + "This operation might be long and can't be interrupted.\n\n" + "Any unsaved changes made to building models and textures in this session will be lost." + "\nProceed?", "About to unpack Building NARCs", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (d == DialogResult.Yes) { unpackBuildingEditorNARCs(forceUnpack: true); MessageBox.Show("Operation completed.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); Helpers.statusLabelMessage(); if (EditorPanels.mapEditor.mapEditorIsReady) { EditorPanels.mapEditor.updateBuildingListComboBox(EditorPanels.mapEditor.interiorbldRadioButton.Checked); } Update(); } } private void diamondAndPearlToolStripMenuItem_Click(object sender, EventArgs e) { OpenCommandsDatabase(RomInfo.BuildCommandNamesDatabase(GameFamilies.DP), RomInfo.BuildCommandParametersDatabase(GameFamilies.DP), RomInfo.BuildActionNamesDatabase(GameFamilies.DP), RomInfo.BuildComparisonOperatorsDatabase(GameFamilies.DP)); } private void platinumToolStripMenuItem_Click(object sender, EventArgs e) { OpenCommandsDatabase(RomInfo.BuildCommandNamesDatabase(GameFamilies.Plat), RomInfo.BuildCommandParametersDatabase(GameFamilies.Plat), RomInfo.BuildActionNamesDatabase(GameFamilies.Plat), RomInfo.BuildComparisonOperatorsDatabase(GameFamilies.Plat)); } private void heartGoldAndSoulSilverToolStripMenuItem_Click(object sender, EventArgs e) { OpenCommandsDatabase(RomInfo.BuildCommandNamesDatabase(GameFamilies.HGSS), RomInfo.BuildCommandParametersDatabase(GameFamilies.HGSS), RomInfo.BuildActionNamesDatabase(GameFamilies.HGSS), RomInfo.BuildComparisonOperatorsDatabase(GameFamilies.HGSS)); } private void manageDatabasesToolStripMenuItem_Click(object sender, EventArgs e) { using (CustomScrcmdManager editor = new CustomScrcmdManager()) editor.Show(); } private void mainTabControl_SelectedIndexChanged(object sender, EventArgs e) { if (mainTabControl.SelectedTab == headerEditorTabPage) { headerEditor.SetupHeaderEditor(this); } else if (mainTabControl.SelectedTab == EditorPanels.matrixEditorTabPage) { matrixEditor.SetupMatrixEditor(this); } else if (mainTabControl.SelectedTab == mapEditorTabPage) { mapEditor.SetupMapEditor(this); } else if (mainTabControl.SelectedTab == EditorPanels.nsbtxEditorTabPage) { nsbtxEditor.SetupNSBTXEditor(this); } else if (mainTabControl.SelectedTab == EditorPanels.eventEditorTabPage) { eventEditor.eventOpenGlControl.MakeCurrent(); eventEditor.SetupEventEditor(this); } else if (mainTabControl.SelectedTab == EditorPanels.textEditorTabPage) { textEditor.SetupTextEditor(this); } else if (mainTabControl.SelectedTab == EditorPanels.trainerEditorTabPage) { trainerEditor.SetupTrainerEditor(this); } else if (mainTabControl.SelectedTab == EditorPanels.tabPageTableEditor) { EditorPanels.headerEditor.resetHeaderSearch(); tableEditor.SetupConditionalMusicTable(this); tableEditor.SetupBattleEffectsTables(this); } else if (mainTabControl.SelectedTab == EditorPanels.scriptEditorTabPage) { scriptEditor.SetupScriptEditor(this); } else if (mainTabControl.SelectedTab == EditorPanels.levelScriptEditorTabPage) { levelScriptEditor.SetUpLevelScriptEditor(this); } else if (mainTabControl.SelectedTab == EditorPanels.tabPageEncountersEditor) { encountersEditor.SetupEncountersEditor(); } else if (mainTabControl.SelectedTab == cameraEditorTabPage) { cameraEditor.SetupCameraEditor(this); } } private void spawnEditorToolStripButton_Click(object sender, EventArgs e) { matrixEditor.SetupMatrixEditor(this); using (SpawnEditor ed = new SpawnEditor(EditorPanels.headerEditor.headerListBoxNames)) { ed.ShowDialog(); } } private void spawnEditorToolStripMenuItem_Click(object sender, EventArgs e) { spawnEditorToolStripButton_Click(null, null); } private void wildEditorButton_Click(object sender, EventArgs e) { openWildEditor(loadCurrent: false); } public void openWildEditor(bool loadCurrent) { Helpers.statusLabelMessage("Attempting to extract Wild Encounters NARC..."); Update(); DSUtils.TryUnpackNarcs(new List() { DirNames.encounters, DirNames.monIcons }); Helpers.statusLabelMessage("Passing control to Wild Pokémon Editor..."); Update(); int encToOpen = loadCurrent ? (int)EditorPanels.headerEditor.wildPokeUpDown.Value : 0; string wildPokeUnpackedPath = gameDirs[DirNames.encounters].unpackedDir; switch (RomInfo.gameFamily) { case GameFamilies.DP: case GameFamilies.Plat: using (WildEditorDPPt editor = new WildEditorDPPt(wildPokeUnpackedPath, RomInfo.GetPokemonNames(), encToOpen, EditorPanels.headerEditor.internalNames.Count)) editor.ShowDialog(); break; default: using (WildEditorHGSS editor = new WildEditorHGSS(wildPokeUnpackedPath, RomInfo.GetPokemonNames(), encToOpen, EditorPanels.headerEditor.internalNames.Count)) editor.ShowDialog(); break; } Helpers.statusLabelMessage(); } #endregion #region Tooltrip Menu private void unpackToFolderToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog of = new OpenFileDialog { Filter = "NARC File (*.narc)|*.narc|All files (*.*)|*.*" }; if (of.ShowDialog(this) != DialogResult.OK) { return; } Narc userfile = Narc.Open(of.FileName); if (userfile is null) { MessageBox.Show("The file you selected is not a valid NARC.", "Cannot proceed", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } MessageBox.Show("Choose where to save the NARC content.\nDSPRE will automatically make a subdirectory.", "Choose destination path", MessageBoxButtons.OK, MessageBoxIcon.Information); CommonOpenFileDialog narcDir = new CommonOpenFileDialog { IsFolderPicker = true, Multiselect = false }; if (narcDir.ShowDialog() != CommonFileDialogResult.Ok) { return; } string finalExtractedPath = narcDir.FileName + "\\" + Path.GetFileNameWithoutExtension(of.FileName); userfile.ExtractToFolder(finalExtractedPath); MessageBox.Show("The contents of " + of.FileName + " have been extracted and saved.", "NARC Extracted", MessageBoxButtons.OK, MessageBoxIcon.Information); DialogResult d = MessageBox.Show("Do you want to rename the files according to their contents?", "Waiting for user", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (d.Equals(DialogResult.Yes)) { ContentBasedBatchRename(this, new DirectoryInfo(finalExtractedPath)); } } private void buildFromFolderToolStripMenuItem_Click(object sender, EventArgs e) { CommonOpenFileDialog narcDir = new CommonOpenFileDialog { IsFolderPicker = true, Multiselect = false }; if (narcDir.ShowDialog() != CommonFileDialogResult.Ok) { return; } MessageBox.Show("Choose where to save the output NARC file.", "Name your NARC file", MessageBoxButtons.OK, MessageBoxIcon.Information); SaveFileDialog sf = new SaveFileDialog { Filter = "NARC File (*.narc)|*.narc", FileName = Path.GetFileName(narcDir.FileName) }; if (sf.ShowDialog(this) != DialogResult.OK) { return; } Narc.FromFolder(narcDir.FileName).Save(sf.FileName); MessageBox.Show("The contents of folder \"" + narcDir.FileName + "\" have been packed.", "NARC Created", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void listBasedToolStripMenuItem_Click(object sender, EventArgs e) { (DirectoryInfo d, FileInfo[] files) dirData = OpenNonEmptyDir(title: "List-Based Batch Rename Tool"); DirectoryInfo d = dirData.d; FileInfo[] files = dirData.files; if (d == null || files == null) { return; } /*==================================================================*/ MessageBox.Show("Choose your enumeration text file.", "Input list file", MessageBoxButtons.OK, MessageBoxIcon.Information); OpenFileDialog of = new OpenFileDialog { Filter = "List File (*.txt; *.list)|*.txt;*.list" }; if (of.ShowDialog(this) != DialogResult.OK) { return; } /*==================================================================*/ const string COMMENT_CHAR = "#"; const string ISOLATED_FOLDERNAME = "DSPRE_IsolatedFiles"; string[] listLines = File.ReadAllLines(of.FileName); listLines = listLines.Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith(COMMENT_CHAR)).ToArray(); if (listLines.Length <= 0) { MessageBox.Show("The enumeration text file you selected is empty or only contains comment lines.\nCan't proceed.", "Invalid list file", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } string msg = "About to process "; int tot; string extra = ""; int diff = files.Length - listLines.Length; if (diff < 0) { //listLines.Length > files.Length tot = files.Length; extra = "(Please note that the length of the chosen list [" + listLines.Length + " entries] " + "exceeds the number of files in the folder.)" + "\n\n"; } else if (diff == 0) { //listLines.Length == files.Length tot = files.Length; } else { // diff > 0 --> listLines.Length < files.Length tot = listLines.Length; extra = "(Please note that there aren't enough entries in the list to rename all files in the chosen folder.\n" + diff + " file" + (diff > 1 ? "s" : "") + " won't be renamed.)" + "\n\n"; } msg += tot + " file" + (tot > 1 ? "s" : ""); DialogResult dr = MessageBox.Show(msg + " from the input folder (taken in ascending order), " + "according to the list file you provided.\n" + "If a destination file already exists, DSPRE will append a number to its name.\n\n" + extra + "Do you want to proceed?", "Confirm operation", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (dr.Equals(DialogResult.Yes)) { int i; for (i = 0; i < tot; i++) { FileInfo f = files[i]; string destName = Path.GetDirectoryName(f.FullName) + "\\" + listLines[i]; if (string.IsNullOrWhiteSpace(destName)) { continue; } File.Move(f.FullName, MakeUniqueName(destName)); } MessageBox.Show("The contents of folder \"" + d.FullName + "\" have been renamed according to " + "\"" + of.FileName + "\".", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); if (listLines.Length < files.Length) { dr = MessageBox.Show("Do you want to isolate the unnamed files by moving them to a dedicated folder?", "Waiting for user", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (dr.Equals(DialogResult.Yes)) { string isolatedDir = d.FullName + "\\" + ISOLATED_FOLDERNAME; if (Directory.Exists(isolatedDir)) { Directory.Delete(isolatedDir); } Directory.CreateDirectory(d.FullName + "\\" + ISOLATED_FOLDERNAME); while (i < files.Length) { FileInfo f = files[i]; string destName = d.FullName + "\\" + ISOLATED_FOLDERNAME + "\\" + f.Name; File.Move(f.FullName, destName); i++; } MessageBox.Show("Isolated files have been moved to " + "\"" + ISOLATED_FOLDERNAME + "\"", "Files moved", MessageBoxButtons.OK, MessageBoxIcon.Information); } } } } private void contentBasedToolStripMenuItem_Click(object sender, EventArgs e) { ContentBasedBatchRename(this); } private void addressHelperToolStripMenuItem_Click(object sender, EventArgs e) { AddressHelper form = new AddressHelper(); form.Show(); } private void overworldEditorToolStripMenuItem_Click(object sender, EventArgs e) { BtxEditor form = new BtxEditor(); form.Show(); } private void generateCSVToolStripMenuItem_Click(object sender, EventArgs e) { Helpers.statusLabelMessage("Exporting to CSV..."); Update(); DocTool.ExportAll(); Helpers.statusLabelMessage(); Update(); } private void flyWarpEditorToolStripMenuItem_Click(object sender, EventArgs e) { var flyEditor = new FlyEditor(gameFamily, EditorPanels.headerEditor.headerListBoxNames); flyEditor.Show(); } private void tradeEditorToolStripMenuItem_Click(object sender, EventArgs e) { DSUtils.TryUnpackNarcs(new List { DirNames.tradeData }); TradeEditor tradeEditor = new TradeEditor(); tradeEditor.Show(); } private void itemEditorToolStripMenuItem_Click(object sender, EventArgs e) { Helpers.statusLabelMessage("Setting up Item Data Editor..."); Update(); DSUtils.TryUnpackNarcs(new List { DirNames.itemData }); DSUtils.TryUnpackNarcs(new List { DirNames.itemIcons }); ItemEditor itemEditor = new ItemEditor( RomInfo.GetItemNames() ); itemEditor.Show(); Helpers.statusLabelMessage(); Update(); } private void fromFolderContentsToolStripMenuItem_Click(object sender, EventArgs e) { (DirectoryInfo d, FileInfo[] files) dirData = OpenNonEmptyDir(title: "Folder-Based List Builder"); DirectoryInfo d = dirData.d; FileInfo[] filePaths = dirData.files; if (d == null || filePaths == null) { return; } MessageBox.Show("Choose where to save the output list file.", "Name your list file", MessageBoxButtons.OK, MessageBoxIcon.Information); SaveFileDialog sf = new SaveFileDialog { Filter = "List File (*.txt; *.list)|*.txt;*.list", FileName = d.Name + ".list" }; if (sf.ShowDialog(this) != DialogResult.OK) { return; } File.WriteAllLines(sf.FileName, new string[] { "#============================================================================", "# File enumeration definition for folder " + "\"" + d.Name + "\"", "#============================================================================" }); File.AppendAllLines(sf.FileName, filePaths.Select(f => f.Name).ToArray()); MessageBox.Show("List file saved.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void enumBasedListBuilderToolStripButton_Click(object sender, EventArgs e) { MessageBox.Show("Pick a C Enum File [with entries on different lines].", "Enum-Based List Builder", MessageBoxButtons.OK, MessageBoxIcon.Information); OpenFileDialog of = new OpenFileDialog { Filter = "Any Text File(*.*)|*.*" }; if (of.ShowDialog(this) != DialogResult.OK) { return; } try { Dictionary entries = new Dictionary(); string[] cFileLines = File.ReadAllLines(of.FileName); cFileLines = cFileLines.Select(x => x.Trim()).ToArray(); int enumStartLine; for (enumStartLine = 0; enumStartLine < cFileLines.Length; enumStartLine++) { if (cFileLines[enumStartLine].Replace(" ", "").Contains("enum{")) { break; } } if (cFileLines.Length - 1 == enumStartLine) { MessageBox.Show("Abrupt termination of enum file.\nAborting.", "Parser error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } int terminationLine; for (terminationLine = enumStartLine + 1; terminationLine < cFileLines.Length; terminationLine++) { if (cFileLines[terminationLine].Replace(" ", "").Contains("};")) { break; } } if (terminationLine >= cFileLines.Length - 1) { MessageBox.Show("Enum file is malformed.\nAborting", "Parser error", MessageBoxButtons.OK, MessageBoxIcon.Error); } ; if (terminationLine - enumStartLine <= 2) { MessageBox.Show("This utility needs at least 2 enum entries.\nAborting.", "Parser error", MessageBoxButtons.OK, MessageBoxIcon.Error); } int indexFirstDifferentChar = cFileLines[enumStartLine + 1].Zip(cFileLines[enumStartLine + 2], (char1, char2) => char1 == char2).TakeWhile(b => b).Count(); int lastCommonUnderscore = cFileLines[enumStartLine + 1].Substring(0, indexFirstDifferentChar).LastIndexOf('_'); int lastNumber = 0; MessageBox.Show("Choose where to save the output list file.", "Name your list file", MessageBoxButtons.OK, MessageBoxIcon.Information); string shortFileName = Path.GetFileNameWithoutExtension(of.FileName); SaveFileDialog sf = new SaveFileDialog { Filter = "List File (*.txt; *.list)|*.txt;*.list", FileName = shortFileName + ".list" }; if (sf.ShowDialog(this) != DialogResult.OK) { return; } for (int s = enumStartLine + 1; s < terminationLine; s++) { string withoutComment; int indexOfComment = cFileLines[s].IndexOf("//"); if (indexOfComment > 0) { withoutComment = cFileLines[s].Substring(0, indexOfComment); } else { withoutComment = cFileLines[s]; } string differentSubstring = withoutComment.Substring(lastCommonUnderscore + 1).Trim().Replace(",", ""); int indexOfEquals = differentSubstring.LastIndexOf('='); string entry = differentSubstring.Substring(0, indexOfEquals).Trim(); if (indexOfEquals > 0) { string numstr = differentSubstring.Substring(indexOfEquals + 1); string[] split = numstr.Split(new char[] { ' ' }, options: StringSplitOptions.RemoveEmptyEntries); if (split.Length > 1) { throw new Exception(); } lastNumber = int.Parse(split[0]); } int posOfUnderscore = entry.LastIndexOf('_'); if (posOfUnderscore >= 0) { entry = entry.Remove(posOfUnderscore, 1).Insert(posOfUnderscore, "."); } entries.Add(lastNumber, entry); lastNumber++; } IEnumerable> sortedEntries = entries.OrderBy(kvp => kvp.Key); File.WriteAllLines(sf.FileName, new string[] { "#============================================================================", "# File enumeration definition based on " + "\"" + shortFileName + "\"", "#============================================================================" }); File.AppendAllLines(sf.FileName, sortedEntries.Select(kvp => kvp.Value)); MessageBox.Show("List file saved.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show("The input enum file couldn't be read correctly.\nNo output file has been written." + "\n\nAborting.", "Parser error", MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBox.Show("Details: " + ex.Message, "Failure details", MessageBoxButtons.OK, MessageBoxIcon.Information); } } private void simpleToolStripMenuItem_MouseDown(object sender, MouseEventArgs e) { ToolStripMenuItem tsmi = (sender as ToolStripMenuItem); SetMenuLayout((byte)tsmi.GetCurrentParent().Items.IndexOf(tsmi)); } private void overlayEditorToolStripMenuItem_Click(object sender, EventArgs e) { Helpers.statusLabelMessage("Setting up Overlay Editor..."); Update(); OverlayEditor ovlEditor = new OverlayEditor(); ovlEditor.Show(); Helpers.statusLabelMessage(); Update(); } private void moveDataEditorToolStripMenuItem_Click(object sender, EventArgs e) { Helpers.statusLabelMessage("Setting up Move Data Editor..."); Update(); DSUtils.TryUnpackNarcs(new List { DirNames.moveData }); string[] moveDescriptions = new TextArchive(RomInfo.moveDescriptionsTextNumbers).messages.Select( x => x.Replace("\\n", Environment.NewLine)).ToArray(); MoveDataEditor mde = new MoveDataEditor( new TextArchive(RomInfo.moveNamesTextNumbers).messages.ToArray(), moveDescriptions ); mde.Show(); Helpers.statusLabelMessage(); Update(); } private void settingsToolStripMenuItem_Click(object sender, EventArgs e) { SettingsWindow editor = new SettingsWindow(); editor.Show(); } private void pokemonDataEditorToolStripMenuItem_Click(object sender, EventArgs e) { string[] itemNames = RomInfo.GetItemNames(); string[] abilityNames = RomInfo.GetAbilityNames(); string[] moveNames = RomInfo.GetAttackNames(); Helpers.statusLabelMessage("Setting up Pokémon Data Editor..."); Update(); DSUtils.TryUnpackNarcs(new List { DirNames.personalPokeData, DirNames.learnsets, DirNames.evolutions, DirNames.monIcons }); RomInfo.SetMonIconsPalTableAddress(); PokemonEditor pde = new PokemonEditor(itemNames, abilityNames, moveNames); Helpers.statusLabelMessage(); Update(); pde.Show(); } #endregion private void trainerEditorStatButton_Click(object sender, EventArgs e) { string[] trcNames = RomInfo.GetTrainerClassNames(); string[] pokeNames = RomInfo.GetPokemonNames(); string[] trainerNames = GetSimpleTrainerNames(); for (int i = 0; i < trcNames.Length; i++) { trcNames[i] = trcNames[i].Replace("♂", " M").Replace("♀", " F"); } Dictionary> trainerUsage = new Dictionary>(); for (int i = 0; i < trainerNames.Length; i++) { if (trainerNames[i].Equals("Angelica") || trainerNames[i].Equals("Mickey")) { continue; } string suffix = "\\" + i.ToString("D4"); TrainerFile f = new TrainerFile( new TrainerProperties( (ushort)trainerComboBox.SelectedIndex, new FileStream(RomInfo.gameDirs[DirNames.trainerProperties].unpackedDir + suffix, FileMode.Open) ), new FileStream(RomInfo.gameDirs[DirNames.trainerParty].unpackedDir + suffix, FileMode.Open), trainerNames[i] ); if (f.party.CountNonEmptyMons() == 0) { continue; } string className = trcNames[f.trp.trainerClass]; if (trainerUsage.TryGetValue(className, out Dictionary innerDict) == false) { innerDict = trainerUsage[className] = new Dictionary(); } for (int p = 0; p < f.trp.partyCount; p++) { PartyPokemon pp = f.party[p]; if (pp.CheckEmpty()) { continue; } string pokeName = pokeNames[(int)pp.pokeID]; if (innerDict.TryGetValue(pokeName, out int occurrences)) { innerDict[pokeName]++; } else { innerDict[pokeName] = 1; } } } Helpers.ExportTrainerUsageToCSV(trainerUsage, "Report.csv"); } #region Poppout Buttons private readonly Dictionary _popouts = new Dictionary(); void WireEditorsPopout() { Register(EditorPanels.scriptEditor, scriptEditorPoppedOutLabel, popoutScriptEditorButton); Register(EditorPanels.levelScriptEditor, LSEditorPoppedOutLabel, popoutLevelScriptEditorButton); Register(EditorPanels.textEditor, textEditorPoppedOutLabel, popoutTextEditorButton); Register(EditorPanels.trainerEditor, trainerEditorPoppedOutLabel, popoutTrainerEditorButton); Register(EditorPanels.nsbtxEditor, nsbtxEditorPopOutLabel, popoutNsbtxEditorButton); Register(EditorPanels.eventEditor, eventEditorPopOutLabel, popoutEventEditorButton); } void Register(Control control, Label lbl, Button btn) { var cfg = new EditorPopoutConfig(control, lbl, btn); _popouts[btn] = cfg; btn.Click += popoutEditorClickHandler; } private void popoutEditorClickHandler(object sender, EventArgs e) { var currentTabInfos = EditorPanels.mainTabControl.SelectedTab; if (sender is Button btn && _popouts.TryGetValue(btn, out var cfg)) Helpers.PopOutEditor(cfg.Control, currentTabInfos.Text, cfg.PlaceholderLabel, cfg.PopoutButton, mainTabImageList.Images[currentTabInfos.ImageIndex]); } #endregion } }