DS-Pokemon-Rom-Editor/DS_Map/Main Window.cs
2025-08-09 03:07:24 +02:00

1966 lines
80 KiB
C#

using DSPRE.Editors;
using DSPRE.Resources;
using DSPRE.ROMFiles;
using Ekona.Images;
using Images;
using LibNDSFormats.NSBMD;
using LibNDSFormats.NSBTX;
using Microsoft.WindowsAPICodePack.Dialogs;
using NarcAPI;
using NSMBe4.NSBMD;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NarcAPI;
using Tao.OpenGl;
using LibNDSFormats.NSBMD;
using LibNDSFormats.NSBTX;
using DSPRE.Resources;
using DSPRE.ROMFiles;
using static DSPRE.RomInfo;
using Images;
using Ekona.Images;
using Microsoft.WindowsAPICodePack.Dialogs;
using ScintillaNET;
using ScintillaNET.Utils;
using System.Globalization;
using static DSPRE.ROMFiles.Event;
using NSMBe4.NSBMD;
using System.Reflection;
using System.ComponentModel;
using DSPRE.Editors;
using DSPRE.Editors.BtxEditor;
using static DSPRE.Helpers;
using Velopack.Sources;
using Velopack;
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.CloneAndSetupDatabase();
EditorPanels.Initialize(this);
Helpers.Initialize(this);
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;
public bool wslDetected = false; // Not technically necessary rn, but it might be useful in the future
/* ROM Information */
public static string gameCode;
public static byte europeByte;
public RomInfo romInfo;
public Dictionary<ushort /*evFile*/, ushort /*header*/> eventToHeader = new Dictionary<ushort, ushort>();
#endregion
#region Subroutines
private void SetMenuLayout(byte layoutStyle)
{
Console.WriteLine("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> { 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();
}
private int UnpackRomCheckUserChoice()
{
// Check if extracted data for the ROM exists, and ask user if they want to load it.
// Returns true if user aborted the process
if (Directory.Exists(RomInfo.workDir))
{
DialogResult d = MessageBox.Show("Extracted data of this ROM has been found.\n" +
"Do you want to load it and unpack it?", "Data detected", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (d == DialogResult.Cancel)
{
return -1; //user wants to abort loading
}
else if (d == DialogResult.Yes)
{
return 0; //user wants to load data
}
else
{
DialogResult nd = MessageBox.Show("All data of this ROM will be re-extracted. Proceed?\n",
"Existing data will be deleted", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (nd == DialogResult.No)
{
return -1; //user wants to abort loading
}
else
{
return 1; //user wants to re-extract data
}
}
}
else
{
return 2; //No data found
}
}
private bool UnpackRom(string ndsFileName)
{
Helpers.statusLabelMessage("Unpacking ROM contents to " + RomInfo.workDir + " ...");
Update();
Directory.CreateDirectory(RomInfo.workDir);
Process unpack = new Process();
unpack.StartInfo.FileName = @"Tools\ndstool.exe";
unpack.StartInfo.Arguments = "-x " + '"' + ndsFileName + '"'
+ " -9 " + '"' + RomInfo.arm9Path + '"'
+ " -7 " + '"' + RomInfo.workDir + "arm7.bin" + '"'
+ " -y9 " + '"' + RomInfo.workDir + "y9.bin" + '"'
+ " -y7 " + '"' + RomInfo.workDir + "y7.bin" + '"'
+ " -d " + '"' + RomInfo.workDir + "data" + '"'
+ " -y " + '"' + RomInfo.workDir + "overlay" + '"'
+ " -t " + '"' + RomInfo.workDir + "banner.bin" + '"'
+ " -h " + '"' + RomInfo.workDir + "header.bin" + '"';
Application.DoEvents();
unpack.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
unpack.StartInfo.CreateNoWindow = true;
try
{
unpack.Start();
unpack.WaitForExit();
}
catch (System.ComponentModel.Win32Exception)
{
MessageBox.Show("Failed to call ndstool.exe" + Environment.NewLine + "Make sure DSPRE's Tools folder is intact.",
"Couldn't unpack ROM", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
return true;
}
#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<ushort, string> namesDict, Dictionary<ushort, byte[]> paramsDict, Dictionary<ushort, string> actionsDict,
Dictionary<ushort, string> 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();
using (BuildingEditor editor = new BuildingEditor(romInfo))
editor.ShowDialog();
}
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<DirNames> toUnpack = new List<DirNames> {
DirNames.exteriorBuildingModels,
DirNames.buildingConfigFiles,
DirNames.buildingTextures,
DirNames.areaData
};
if (forceUnpack)
{
DSUtils.ForceUnpackNarcs(toUnpack);
if (RomInfo.gameFamily == GameFamilies.HGSS)
{
DSUtils.ForceUnpackNarcs(new List<DirNames> { DirNames.interiorBuildingModels });// Last = interior buildings dir
}
}
else
{
DSUtils.TryUnpackNarcs(toUnpack);
if (RomInfo.gameFamily == GameFamilies.HGSS)
{
DSUtils.TryUnpackNarcs(new List<DirNames> { 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)
{
return;
}
// Validate path and check for OneDrive
if (!ValidateFilePath(openRom.FileName))
{
return;
}
if (!detectAndHandleWSL(openRom.FileName))
{
return; // User chose not to create a new work directory
}
// Handle WSL
if (wslDetected)
{
string executablePath = Path.GetDirectoryName(Application.ExecutablePath);
string buildFolderPath = Path.Combine(executablePath, "build");
// Create a new work directory in the same folder as DSPRE
if (!Directory.Exists(buildFolderPath))
{
Directory.CreateDirectory(buildFolderPath);
}
// Copy the ROM to the build folder
string newRomPath = Path.Combine(buildFolderPath, Path.GetFileName(openRom.FileName));
// Check if file already exists and ask to overwrite
if (File.Exists(newRomPath))
{
DialogResult overwriteResult = MessageBox.Show("The ROM file already exists in the build folder. Do you want to overwrite it?", "File Exists", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (overwriteResult != DialogResult.Yes)
{
return; // User chose not to overwrite
}
}
try
{
File.Copy(openRom.FileName, newRomPath, true);
openRom.FileName = newRomPath; // Update the file name to the new path
}
catch (IOException ex)
{
MessageBox.Show("Failed to copy ROM to build folder: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
SetupROMLanguage(openRom.FileName);
/* Set ROM gameVersion and language */
romInfo = new RomInfo(gameCode, openRom.FileName, useSuffix: true);
if (string.IsNullOrWhiteSpace(RomInfo.romID) || string.IsNullOrWhiteSpace(RomInfo.fileName))
{
return;
}
CheckROMLanguage();
int userchoice = UnpackRomCheckUserChoice();
switch (userchoice)
{
case -1:
Helpers.statusLabelMessage("Loading aborted");
Update();
return;
case 0:
break;
case 1:
case 2:
Application.DoEvents();
if (userchoice == 1)
{
Helpers.statusLabelMessage("Deleting old data...");
try
{
Directory.Delete(RomInfo.workDir, true);
}
catch (IOException)
{
MessageBox.Show("Concurrent access detected: \n" + RomInfo.workDir +
"\nMake sure no other process is using the extracted ROM folder while DSPRE is running.", "Concurrent Access", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
Update();
}
try
{
if (!UnpackRom(openRom.FileName))
{
Helpers.statusLabelError("ERROR");
languageLabel.Text = "";
versionLabel.Text = "Error";
return;
}
ARM9.EditSize(-12);
}
catch (IOException)
{
MessageBox.Show("Can't access temp directory: \n" + RomInfo.workDir + "\nThis might be a temporary issue.\nMake sure no other process is using it and try again.", "Open Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Helpers.statusLabelError("ERROR: Concurrent access to " + RomInfo.workDir);
Update();
return;
}
break;
}
iconON = true;
gameIcon.Refresh(); // Paint game icon
Helpers.statusLabelMessage("Attempting to unpack NARCs from folder...");
Update();
//for (int i = 0; i < 128; i++) {
// if (OverlayUtils.IsCompressed(i)) {
// OverlayUtils.Decompress(i);
// }
//}
ReadROMInitData();
}
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 true; // No WSL detected, proceed normally
}
if (Directory.Exists(fullPath))
{
MessageBox.Show("WSL was detected in the path. " +
"The associated unpacked folder of a ROM should not be stored on the WSL file system! " +
"Please move the folder to the same drive that DSPRE is located on.", "Invalid Path", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
DialogResult result = MessageBox.Show("WSL was detected in the path. " +
"Do you want to create a build directory in the same folder as DSPRE to unpack to?", "WSL Detected", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
wslDetected = true;
if (result == DialogResult.Yes)
{
return true; // User wants to create a new work directory
}
else
{
MessageBox.Show("Unpacking will not be possible without a valid work directory.", "Unpacking aborted", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
private bool DetectRequiredTools()
{
bool toolsMissing = false;
List<string> missingToolsList = new List<string>();
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 (europeByte == 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;
}
if (!detectAndHandleWSL(romFolderPath))
{
AppLogger.Info("ROM path validation failed. Possibly invalid or on a restricted (WSL).");
return;
}
try
{
string headerFile = Directory.GetFiles(romFolderPath).First(x => x.Contains("header.bin"));
AppLogger.Debug($"Found header file: {headerFile}");
SetupROMLanguage(headerFile);
}
catch (InvalidOperationException)
{
AppLogger.Error("No 'header.bin' file found in ROM folder. Cannot initialize ROM.");
MessageBox.Show("This folder does not seem to contain any data from a NDS Pokémon ROM.", "No ROM Data", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
romInfo = new RomInfo(gameCode, romFolderPath, useSuffix: false);
if (string.IsNullOrWhiteSpace(RomInfo.romID) || string.IsNullOrWhiteSpace(RomInfo.fileName))
{
AppLogger.Error("ROM ID or filename is empty after initialization. Aborting.");
return;
}
AppLogger.Info($"ROM loaded successfully: ID = {RomInfo.romID}, Name = {RomInfo.fileName}");
CheckROMLanguage();
AppLogger.Debug("ROM language checked and applied.");
iconON = true;
gameIcon.Refresh(); // Paint game icon
AppLogger.Debug("Game icon refreshed.");
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;
europeByte = br.ReadByte();
}
}
private void ReadROMInitData()
{
if (ARM9.CheckCompressionMark())
{
if (!RomInfo.gameFamily.Equals(GameFamilies.HGSS))
{
MessageBox.Show("Unexpected compressed ARM9. It is advised that you double check the ARM9.");
}
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;
}
}
/* 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.fileName;
}
private void saveRom_Click(object sender, EventArgs e)
{
SaveFileDialog saveRom = new SaveFileDialog
{
Filter = DSUtils.NDSRomFilter,
InitialDirectory = SettingsManager.Settings.exportPath
};
if (saveRom.ShowDialog(this) != DialogResult.OK)
{
return;
}
Helpers.statusLabelMessage("Repacking NARCS...");
Update();
var dateBegin = DateTime.Now;
// Repack NARCs
foreach (KeyValuePair<DirNames, (string packedDir, string unpackedDir)> 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);
// }
//}
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);
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<DirNames>().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.ShowDialog();
}
private void mainTabControl_SelectedIndexChanged(object sender, EventArgs e) {
if (mainTabControl.SelectedTab == headerEditorTabPage) {
//
}
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 tabPageScriptEditor_Enter(object sender, EventArgs e)
{
scriptEditor.SetupScriptEditor(this);
}
private void tabPageLevelScriptEditor_Enter(object sender, EventArgs e)
{
levelScriptEditor.SetUpLevelScriptEditor(this);
}
private void tabPageEncountersEditor_Enter(object sender, EventArgs e)
{
encountersEditor.SetupEncountersEditor();
}
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>() { 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];
Console.WriteLine(f.Name);
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];
Console.WriteLine(f.Name);
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> { 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> { DirNames.itemData });
DSUtils.TryUnpackNarcs(new List<DirNames> { DirNames.itemIcons });
ItemEditor itemEditor = new ItemEditor(
RomInfo.GetItemNames()
);
itemEditor.ShowDialog();
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<int, string> entries = new Dictionary<int, string>();
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<KeyValuePair<int, string>> 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.ShowDialog();
Helpers.statusLabelMessage();
Update();
}
private void moveDataEditorToolStripMenuItem_Click(object sender, EventArgs e)
{
Helpers.statusLabelMessage("Setting up Move Data Editor...");
Update();
DSUtils.TryUnpackNarcs(new List<DirNames> { 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.ShowDialog();
Helpers.statusLabelMessage();
Update();
}
private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
{
using (SettingsWindow editor = new SettingsWindow())
editor.ShowDialog();
}
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> { DirNames.personalPokeData, DirNames.learnsets, DirNames.evolutions, DirNames.monIcons });
RomInfo.SetMonIconsPalTableAddress();
PokemonEditor pde = new PokemonEditor(itemNames, abilityNames, moveNames);
Helpers.statusLabelMessage();
Update();
pde.ShowDialog();
}
#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<string, Dictionary<string, int>> trainerUsage = new Dictionary<string, Dictionary<string, int>>();
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<string, int> innerDict) == false)
{
innerDict = trainerUsage[className] = new Dictionary<string, int>();
}
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 void popoutTextEditorButton_Click(object sender, EventArgs e)
{
textEditorPoppedOutLabel.Visible = true; // Show Editor popped-out label
popoutTextEditorButton.Enabled = false; // Disable popout button
Helpers.PopOutEditor(textEditor, "Text Editor", mainTabImageList.Images[4], ctrl =>
{
textEditorPoppedOutLabel.Visible = false; // Hide Editor popped-out label
popoutTextEditorButton.Enabled = true; // Enable popout button
});
}
private void popoutLevelScriptEditorButton_Click(object sender, EventArgs e)
{
LSEditorPoppedOutLabel.Visible = true; // Show Editor popped-out label
popoutLevelScriptEditorButton.Enabled = false; // Disable popout button
Helpers.PopOutEditor(levelScriptEditor, "Level Script Editor", mainTabImageList.Images[3], ctrl =>
{
LSEditorPoppedOutLabel.Visible = false; // Hide Editor popped-out label
popoutLevelScriptEditorButton.Enabled = true; // Enable popout button
});
}
private void popoutScriptEditorButton_Click(object sender, EventArgs e)
{
scriptEditorPoppedOutLabel.Visible = true; // Show Editor popped-out label
popoutScriptEditorButton.Enabled = false; // Disable popout button
Helpers.PopOutEditor(scriptEditor, "Script Editor", mainTabImageList.Images[3], ctrl =>
{
scriptEditorPoppedOutLabel.Visible = false; // Hide Editor popped-out label
popoutScriptEditorButton.Enabled = true; // Enable popout button
});
}
private void popoutTrainerEditorButton_Click(object sender, EventArgs e)
{
trainerEditorPoppedOutLabel.Visible = true; // Show Editor popped-out label
popoutTrainerEditorButton.Enabled = false; // Disable popout button
Helpers.PopOutEditor(trainerEditor, "Trainer Editor", mainTabImageList.Images[8], ctrl =>
{
trainerEditorPoppedOutLabel.Visible = false; // Hide Editor popped-out label
popoutTrainerEditorButton.Enabled = true; // Enable popout button
});
}
#endregion
}
}