using DSPRE.ROMFiles; using Ekona.Images; using MKDS_Course_Editor.Export3DTools; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using static DSPRE.RomInfo; using static System.Windows.Forms.VisualStyles.VisualStyleElement.TextBox; namespace DSPRE.Editors { public partial class TableEditor : UserControl { MainProgram _parent; public bool battleTableEditorIsReady { get; set; } = false; public bool conditionnalMusicTableEditorIsReady { get; set; } = false; public TableEditor() { InitializeComponent(); } #region Table Editor #region Variables string[] pokeNames; string[] trcNames; List<(ushort header, ushort flag, ushort music)> conditionalMusicTable; uint conditionalMusicTableStartAddress; public List<(int trainerClass, int comboID)> vsTrainerEffectsList; List<(int pokemonID, int comboID)> vsPokemonEffectsList; List<(ushort vsGraph, ushort battleSSEQ)> effectsComboTable; uint vsTrainerTableStartAddress; uint vsPokemonTableStartAddress; uint effectsComboMainTableStartAddress; //Show Pokemon Icons private readonly PaletteBase tableEditorMonIconPal; private readonly ImageBase tableEditorMonIconTile; private readonly SpriteBase tableEditorMonIconSprite; #endregion public void SetupConditionalMusicTable(MainProgram parent, bool force = false) { if (conditionnalMusicTableEditorIsReady && !force) { return; } conditionnalMusicTableEditorIsReady = true; this._parent = parent; switch (RomInfo.gameFamily) { case GameFamilies.HGSS: RomInfo.SetConditionalMusicTableOffsetToRAMAddress(); conditionalMusicTable = new List<(ushort, ushort, ushort)>(); conditionalMusicTableStartAddress = BitConverter.ToUInt32(ARM9.ReadBytes(RomInfo.conditionalMusicTableOffsetToRAMAddress, 4), 0) - ARM9.address; byte tableEntriesCount = ARM9.ReadByte(RomInfo.conditionalMusicTableOffsetToRAMAddress - 8); conditionalMusicTableListBox.Items.Clear(); using (ARM9.Reader ar = new ARM9.Reader(conditionalMusicTableStartAddress)) { for (int i = 0; i < tableEntriesCount; i++) { ushort header = ar.ReadUInt16(); ushort flag = ar.ReadUInt16(); ushort musicID = ar.ReadUInt16(); conditionalMusicTable.Add((header, flag, musicID)); conditionalMusicTableListBox.Items.Add(EditorPanels.headerEditor.headerListBox.Items[header]); } } headerConditionalMusicComboBox.Items.Clear(); foreach (string location in EditorPanels.headerEditor.headerListBox.Items) { headerConditionalMusicComboBox.Items.Add(location); } if (conditionalMusicTableListBox.Items.Count > 0) { conditionalMusicTableListBox.SelectedIndex = 0; } break; case GameFamilies.Plat: pbEffectsMonGroupBox.Enabled = false; pbEffectsTrainerGroupBox.Enabled = false; conditionalMusicGroupBox.Enabled = false; break; default: pbEffectsMonGroupBox.Enabled = false; pbEffectsTrainerGroupBox.Enabled = false; conditionalMusicGroupBox.Enabled = false; pbEffectsGroupBox.Enabled = false; break; } } public void SetupBattleEffectsTables(MainProgram parent, bool force = false) { if (battleTableEditorIsReady && !force) { return; } battleTableEditorIsReady = true; this._parent = parent; if (RomInfo.gameFamily == GameFamilies.HGSS || RomInfo.gameFamily == GameFamilies.Plat) { DSUtils.TryUnpackNarcs(new List { DirNames.trainerGraphics, DirNames.textArchives, DirNames.monIcons }); RomInfo.SetBattleEffectsData(); RomInfo.SetMonIconsPalTableAddress(); effectsComboTable = new List<(ushort vsGraph, ushort battleSSEQ)>(); effectsComboMainTableStartAddress = BitConverter.ToUInt32(ARM9.ReadBytes(RomInfo.effectsComboTableOffsetToRAMAddress, 4), 0); PatchToolboxDialog.flag_MainComboTableRepointed = (effectsComboMainTableStartAddress >= RomInfo.synthOverlayLoadAddress); effectsComboMainTableStartAddress -= PatchToolboxDialog.flag_MainComboTableRepointed ? RomInfo.synthOverlayLoadAddress : ARM9.address; byte comboTableEntriesCount; if (RomInfo.gameFamily == GameFamilies.HGSS) { comboTableEntriesCount = ARM9.ReadByte(RomInfo.effectsComboTableOffsetToSizeLimiter); vsPokemonEffectsList = new List<(int pokemonID, int comboID)>(); vsTrainerEffectsList = new List<(int trainerClass, int comboID)>(); vsPokemonTableStartAddress = BitConverter.ToUInt32(ARM9.ReadBytes(RomInfo.vsPokemonEntryTableOffsetToRAMAddress, 4), 0); PatchToolboxDialog.flag_PokemonBattleTableRepointed = (vsPokemonTableStartAddress >= RomInfo.synthOverlayLoadAddress); vsPokemonTableStartAddress -= PatchToolboxDialog.flag_PokemonBattleTableRepointed ? RomInfo.synthOverlayLoadAddress : ARM9.address; vsTrainerTableStartAddress = BitConverter.ToUInt32(ARM9.ReadBytes(RomInfo.vsTrainerEntryTableOffsetToRAMAddress, 4), 0); PatchToolboxDialog.flag_TrainerClassBattleTableRepointed = (vsTrainerTableStartAddress >= RomInfo.synthOverlayLoadAddress); vsTrainerTableStartAddress -= PatchToolboxDialog.flag_TrainerClassBattleTableRepointed ? RomInfo.synthOverlayLoadAddress : ARM9.address; pbEffectsPokemonCombobox.Items.Clear(); pokeNames = RomInfo.GetPokemonNames(); for (int i = 0; i < pokeNames.Length; i++) { pbEffectsPokemonCombobox.Items.Add("[" + i + "]" + " " + pokeNames[i]); } RepopulateTableEditorTrainerClasses(); pbEffectsVsTrainerListbox.Items.Clear(); pbEffectsVsPokemonListbox.Items.Clear(); } else { comboTableEntriesCount = 35; } pbEffectsCombosListbox.Items.Clear(); String expArmPath = RomInfo.gameDirs[DirNames.synthOverlay].unpackedDir + '\\' + PatchToolboxDialog.expandedARMfileID.ToString("D4"); if (RomInfo.gameFamily == GameFamilies.HGSS) { using (DSUtils.EasyReader ar = new DSUtils.EasyReader(PatchToolboxDialog.flag_TrainerClassBattleTableRepointed ? expArmPath : RomInfo.arm9Path, vsTrainerTableStartAddress)) { byte trainerTableEntriesCount = ARM9.ReadByte(RomInfo.vsTrainerEntryTableOffsetToSizeLimiter); for (int i = 0; i < trainerTableEntriesCount; i++) { ushort entry = ar.ReadUInt16(); int classID = entry & 1023; int comboID = entry >> 10; vsTrainerEffectsList.Add((classID, comboID)); pbEffectsVsTrainerListbox.Items.Add(pbEffectsTrainerCombobox.Items[classID] + " uses Combo #" + comboID); } } using (DSUtils.EasyReader ar = new DSUtils.EasyReader(PatchToolboxDialog.flag_PokemonBattleTableRepointed ? expArmPath : RomInfo.arm9Path, vsPokemonTableStartAddress)) { byte pokemonTableEntriesCount = ARM9.ReadByte(RomInfo.vsPokemonEntryTableOffsetToSizeLimiter); for (int i = 0; i < pokemonTableEntriesCount; i++) { ushort entry = ar.ReadUInt16(); int pokeID = entry & 1023; int comboID = entry >> 10; vsPokemonEffectsList.Add((pokeID, comboID)); string pokeName; try { pokeName = pokeNames[pokeID]; } catch (IndexOutOfRangeException) { pokeName = "UNKNOWN"; } pbEffectsVsPokemonListbox.Items.Add("[" + pokeID.ToString("D3") + "]" + " " + pokeName + " uses Combo #" + comboID); } } } using (DSUtils.EasyReader ar = new DSUtils.EasyReader(PatchToolboxDialog.flag_MainComboTableRepointed ? expArmPath : RomInfo.arm9Path, effectsComboMainTableStartAddress)) { for (int i = 0; i < comboTableEntriesCount; i++) { ushort battleIntroEffect = ar.ReadUInt16(); ushort battleMusic = ar.ReadUInt16(); effectsComboTable.Add((battleIntroEffect, battleMusic)); pbEffectsCombosListbox.Items.Add("Combo " + i.ToString("D2") + " - " + "Effect #" + battleIntroEffect + ", " + "Music #" + battleMusic); } } if (RomInfo.gameFamily == GameFamilies.HGSS) { var items = pbEffectsCombosListbox.Items.Cast().ToArray(); pbEffectsPokemonChooseMainCombobox.Items.Clear(); pbEffectsPokemonChooseMainCombobox.Items.AddRange(items); pbEffectsTrainerChooseMainCombobox.Items.Clear(); pbEffectsTrainerChooseMainCombobox.Items.AddRange(items); if (pbEffectsVsTrainerListbox.Items.Count > 0) { pbEffectsVsTrainerListbox.SelectedIndex = 0; } if (pbEffectsVsPokemonListbox.Items.Count > 0) { pbEffectsVsPokemonListbox.SelectedIndex = 0; } } if (pbEffectsCombosListbox.Items.Count > 0) { pbEffectsCombosListbox.SelectedIndex = 0; } } else { pbEffectsGroupBox.Enabled = false; } } private void RepopulateTableEditorTrainerClasses() { pbEffectsTrainerCombobox.Items.Clear(); trcNames = RomInfo.GetTrainerClassNames(); for (int i = 0; i < trcNames.Length; i++) { pbEffectsTrainerCombobox.Items.Add("[" + i.ToString("D3") + "]" + " " + trcNames[i]); } } private void conditionalMusicTableListBox_SelectedIndexChanged(object sender, EventArgs e) { int selection = conditionalMusicTableListBox.SelectedIndex; headerConditionalMusicComboBox.SelectedIndex = conditionalMusicTable[selection].header; Helpers.DisableHandlers(); flagConditionalMusicUpDown.Value = conditionalMusicTable[selection].flag; musicIDconditionalMusicUpDown.Value = conditionalMusicTable[selection].music; Helpers.EnableHandlers(); } private void headerConditionalMusicComboBox_SelectedIndexChanged(object sender, EventArgs e) { if (Helpers.HandlersDisabled) { return; } (ushort header, ushort flag, ushort music) oldTuple = conditionalMusicTable[conditionalMusicTableListBox.SelectedIndex]; (ushort header, ushort flag, ushort music) newTuple = ((ushort)headerConditionalMusicComboBox.SelectedIndex, oldTuple.flag, oldTuple.music); conditionalMusicTable[conditionalMusicTableListBox.SelectedIndex] = newTuple; MapHeader selected = MapHeader.LoadFromARM9(newTuple.header); switch (RomInfo.gameFamily) { case GameFamilies.DP: locationNameConditionalMusicLBL.Text = RomInfo.GetLocationNames()[(selected as HeaderDP).locationName]; break; case GameFamilies.Plat: locationNameConditionalMusicLBL.Text = RomInfo.GetLocationNames()[(selected as HeaderPt).locationName]; break; case GameFamilies.HGSS: locationNameConditionalMusicLBL.Text = RomInfo.GetLocationNames()[(selected as HeaderHGSS).locationName]; break; } } private void flagConditionalMusicUpDown_ValueChanged(object sender, EventArgs e) { if (Helpers.HandlersDisabled) { return; } (ushort header, ushort flag, ushort music) oldTuple = conditionalMusicTable[conditionalMusicTableListBox.SelectedIndex]; conditionalMusicTable[conditionalMusicTableListBox.SelectedIndex] = (oldTuple.header, (ushort)flagConditionalMusicUpDown.Value, oldTuple.music); } private void musicIDconditionalMusicUpDown_ValueChanged(object sender, EventArgs e) { if (Helpers.HandlersDisabled) { return; } (ushort header, ushort flag, ushort music) oldTuple = conditionalMusicTable[conditionalMusicTableListBox.SelectedIndex]; conditionalMusicTable[conditionalMusicTableListBox.SelectedIndex] = (oldTuple.header, oldTuple.flag, (ushort)musicIDconditionalMusicUpDown.Value); } private void HOWconditionalMusicTableButton_Click(object sender, EventArgs e) { MessageBox.Show("For each Location in the list, override Header's music with chosen Music ID, if Flag is set.", "How this table works", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void saveConditionalMusicTableBTN_Click(object sender, EventArgs e) { for (int i = 0; i < conditionalMusicTable.Count; i++) { ARM9.WriteBytes(BitConverter.GetBytes(conditionalMusicTable[i].header), (uint)(conditionalMusicTableStartAddress + 6 * i)); ARM9.WriteBytes(BitConverter.GetBytes(conditionalMusicTable[i].flag), (uint)(conditionalMusicTableStartAddress + 6 * i + 2)); ARM9.WriteBytes(BitConverter.GetBytes(conditionalMusicTable[i].music), (uint)(conditionalMusicTableStartAddress + 6 * i + 4)); } } private void TBLEditortrainerClassPreviewPic_ValueChanged(object sender, EventArgs e) { EditorPanels.trainerEditor.UpdateTrainerClassPic(tbEditorTrClassPictureBox, (int)((NumericUpDown)sender).Value); } private void saveEffectComboBTN_Click(object sender, EventArgs e) { int index = pbEffectsCombosListbox.SelectedIndex; ushort battleIntroEffect = (ushort)pbEffectsVSAnimationUpDown.Value; ushort battleMusic = (ushort)pbEffectsBattleSSEQUpDown.Value; effectsComboTable[index] = (battleIntroEffect, battleMusic); String expArmPath = RomInfo.gameDirs[DirNames.synthOverlay].unpackedDir + '\\' + PatchToolboxDialog.expandedARMfileID.ToString("D4"); using (DSUtils.EasyWriter wr = new DSUtils.EasyWriter(PatchToolboxDialog.flag_MainComboTableRepointed ? expArmPath : RomInfo.arm9Path, effectsComboMainTableStartAddress + 4 * index)) { wr.Write(battleIntroEffect); wr.Write(battleMusic); }; Helpers.DisableHandlers(); string updatedEntry = "Combo " + index.ToString("D2") + " - " + "Effect #" + battleIntroEffect + ", " + "Music #" + battleMusic; pbEffectsCombosListbox.Items[index] = updatedEntry; if (RomInfo.gameFamily == GameFamilies.HGSS) { pbEffectsTrainerChooseMainCombobox.Items[index] = pbEffectsPokemonChooseMainCombobox.Items[index] = updatedEntry; } Helpers.EnableHandlers(); } private void saveVSPokemonEntryBTN_Click(object sender, EventArgs e) { } private void saveVSTrainerEntryBTN_Click(object sender, EventArgs e) { int index = pbEffectsVsTrainerListbox.SelectedIndex; ushort trainerClass = (ushort)pbEffectsTrainerCombobox.SelectedIndex; ushort comboID = (ushort)pbEffectsTrainerChooseMainCombobox.SelectedIndex; vsTrainerEffectsList[index] = (trainerClass, comboID); String expArmPath = RomInfo.gameDirs[DirNames.synthOverlay].unpackedDir + '\\' + PatchToolboxDialog.expandedARMfileID.ToString("D4"); using (DSUtils.EasyWriter wr = new DSUtils.EasyWriter(PatchToolboxDialog.flag_TrainerClassBattleTableRepointed ? expArmPath : RomInfo.arm9Path, vsTrainerTableStartAddress + 2 * index)) { wr.Write((ushort)((trainerClass & 1023) + (comboID << 10))); }; Helpers.DisableHandlers(); pbEffectsVsTrainerListbox.Items[index] = "[" + trainerClass.ToString("D3") + "]" + " " + trcNames[trainerClass] + " uses Combo #" + comboID; Helpers.EnableHandlers(); } private void HOWpbEffectsTableButton_Click(object sender, EventArgs e) { MessageBox.Show("An entry of this table is a combination of VS. Graphics + Battle Theme.\n\n" + (RomInfo.gameFamily.Equals(GameFamilies.HGSS) ? "Each entry can be \"inherited\" by one or more Pokémon or Trainer classes." : ""), "How this table works", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void HOWvsPokemonButton_Click(object sender, EventArgs e) { MessageBox.Show("Each entry of this table links a \"Wild\" Pokémon to an Effect Combo from the Combos Table.\n\n" + "Whenever that Pokémon is encountered in the tall grass or via script command, its VS. Sequence and Battle Theme will be automatically triggered.", "How this table works", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void HOWVsTrainerButton_Click(object sender, EventArgs e) { MessageBox.Show("Each entry of this table links a Trainer Class to an Effect Combo from the Combos Table.\n\n" + "Every Trainer Class with a given combo will start the same VS. Sequence and Battle Theme.", "How this table works", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void pbEffectsVsTrainerListbox_SelectedIndexChanged(object sender, EventArgs e) { int trainerSelection = pbEffectsVsTrainerListbox.SelectedIndex; if (Helpers.HandlersDisabled || trainerSelection < 0) { return; } (int trainerClass, int comboID) entry = vsTrainerEffectsList[trainerSelection]; pbEffectsTrainerCombobox.SelectedIndex = entry.trainerClass; pbEffectsCombosListbox.SelectedIndex = pbEffectsTrainerChooseMainCombobox.SelectedIndex = entry.comboID; tbEditorTrClassFramePreviewUpDown.Value = 0; } private void pbEffectsVsPokemonListbox_SelectedIndexChanged(object sender, EventArgs e) { int pokemonSelection = pbEffectsVsPokemonListbox.SelectedIndex; if (Helpers.HandlersDisabled || pokemonSelection < 0) { return; } (int pokemonID, int comboID) entry = vsPokemonEffectsList[pokemonSelection]; try { pbEffectsPokemonCombobox.SelectedIndex = entry.pokemonID; } catch (ArgumentOutOfRangeException) { pbEffectsPokemonCombobox.SelectedIndex = 0; } pbEffectsCombosListbox.SelectedIndex = pbEffectsPokemonChooseMainCombobox.SelectedIndex = entry.comboID; } private void pbEffectsCombosListbox_SelectedIndexChanged(object sender, EventArgs e) { int comboSelection = pbEffectsCombosListbox.SelectedIndex; if (Helpers.HandlersDisabled || comboSelection < 0) { return; } (ushort vsGraph, ushort battleSSEQ) entry = effectsComboTable[comboSelection]; pbEffectsBattleSSEQUpDown.Value = entry.battleSSEQ; pbEffectsVSAnimationUpDown.Value = entry.vsGraph; } private void pbEffectsTrainerCombobox_SelectedIndexChanged(object sender, EventArgs e) { int maxFrames = EditorPanels.trainerEditor.LoadTrainerClassPic((sender as ComboBox).SelectedIndex); EditorPanels.trainerEditor.UpdateTrainerClassPic(tbEditorTrClassPictureBox); tbEditorTrClassFramePreviewUpDown.Maximum = maxFrames; tbEditortrainerClassFrameMaxLabel.Text = "/" + maxFrames; } private void pbEffectsPokemonCombobox_SelectedIndexChanged(object sender, EventArgs e) { ComboBox cb = sender as ComboBox; tbEditorPokeminiPictureBox.Image = DSUtils.GetPokePic(cb.SelectedIndex, tbEditorPokeminiPictureBox.Width, tbEditorPokeminiPictureBox.Height); tbEditorPokeminiPictureBox.Update(); } #endregion } }