diff --git a/DS_Map/DSPRE.csproj b/DS_Map/DSPRE.csproj index 8b3ca99..131a35c 100644 --- a/DS_Map/DSPRE.csproj +++ b/DS_Map/DSPRE.csproj @@ -122,6 +122,18 @@ + + Form + + + BtxEditor.cs + + + Form + + + BtxExitConfirmation.cs + UserControl @@ -219,6 +231,7 @@ Form + @@ -431,6 +444,12 @@ DVCalcNatureViewerForm.cs + + BtxEditor.cs + + + BtxExitConfirmation.cs + EncountersEditor.cs diff --git a/DS_Map/Editors/BtxEditor/BtxEditor.Designer.cs b/DS_Map/Editors/BtxEditor/BtxEditor.Designer.cs new file mode 100644 index 0000000..3e54d2b --- /dev/null +++ b/DS_Map/Editors/BtxEditor/BtxEditor.Designer.cs @@ -0,0 +1,203 @@ +namespace DSPRE.Editors.BtxEditor +{ + partial class BtxEditor + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.overworldList = new System.Windows.Forms.ComboBox(); + this.overworldPictureBox = new System.Windows.Forms.PictureBox(); + this.showBtxFileButton = new System.Windows.Forms.Button(); + this.exportImagePng = new System.Windows.Forms.Button(); + this.importImagePng = new System.Windows.Forms.Button(); + this.shinyCheckbox = new System.Windows.Forms.CheckBox(); + this.panel1 = new System.Windows.Forms.Panel(); + this.saveSelected_Button = new System.Windows.Forms.Button(); + this.SaveAll_Button = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.overworldPictureBox)).BeginInit(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(13, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(55, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Overworld"; + // + // overworldList + // + this.overworldList.FormattingEnabled = true; + this.overworldList.Location = new System.Drawing.Point(12, 29); + this.overworldList.Name = "overworldList"; + this.overworldList.Size = new System.Drawing.Size(125, 21); + this.overworldList.TabIndex = 1; + this.overworldList.SelectedIndexChanged += new System.EventHandler(this.overworldList_SelectedIndexChanged); + // + // overworldPictureBox + // + this.overworldPictureBox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; + this.overworldPictureBox.Location = new System.Drawing.Point(3, 0); + this.overworldPictureBox.Name = "overworldPictureBox"; + this.overworldPictureBox.Size = new System.Drawing.Size(117, 209); + this.overworldPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; + this.overworldPictureBox.TabIndex = 2; + this.overworldPictureBox.TabStop = false; + // + // showBtxFileButton + // + this.showBtxFileButton.Enabled = false; + this.showBtxFileButton.Image = global::DSPRE.Properties.Resources.lens; + this.showBtxFileButton.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; + this.showBtxFileButton.Location = new System.Drawing.Point(148, 27); + this.showBtxFileButton.Name = "showBtxFileButton"; + this.showBtxFileButton.Size = new System.Drawing.Size(121, 23); + this.showBtxFileButton.TabIndex = 3; + this.showBtxFileButton.Text = "Show BTX File"; + this.showBtxFileButton.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; + this.showBtxFileButton.UseVisualStyleBackColor = true; + this.showBtxFileButton.Click += new System.EventHandler(this.showBtxFileButton_Click); + // + // exportImagePng + // + this.exportImagePng.Enabled = false; + this.exportImagePng.Image = global::DSPRE.Properties.Resources.exportArrow; + this.exportImagePng.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; + this.exportImagePng.Location = new System.Drawing.Point(148, 145); + this.exportImagePng.Name = "exportImagePng"; + this.exportImagePng.Size = new System.Drawing.Size(121, 23); + this.exportImagePng.TabIndex = 4; + this.exportImagePng.Text = "Export PNG"; + this.exportImagePng.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; + this.exportImagePng.UseVisualStyleBackColor = true; + this.exportImagePng.Click += new System.EventHandler(this.exportImagePng_Click); + // + // importImagePng + // + this.importImagePng.Enabled = false; + this.importImagePng.Image = global::DSPRE.Properties.Resources.importArrow; + this.importImagePng.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; + this.importImagePng.Location = new System.Drawing.Point(16, 145); + this.importImagePng.Name = "importImagePng"; + this.importImagePng.Size = new System.Drawing.Size(121, 23); + this.importImagePng.TabIndex = 5; + this.importImagePng.Text = "Import PNG"; + this.importImagePng.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; + this.importImagePng.UseVisualStyleBackColor = true; + this.importImagePng.Click += new System.EventHandler(this.importImagePng_Click); + // + // shinyCheckbox + // + this.shinyCheckbox.AutoSize = true; + this.shinyCheckbox.Enabled = false; + this.shinyCheckbox.Location = new System.Drawing.Point(13, 57); + this.shinyCheckbox.Name = "shinyCheckbox"; + this.shinyCheckbox.Size = new System.Drawing.Size(52, 17); + this.shinyCheckbox.TabIndex = 6; + this.shinyCheckbox.Text = "Shiny"; + this.shinyCheckbox.UseVisualStyleBackColor = true; + this.shinyCheckbox.CheckedChanged += new System.EventHandler(this.shinyCheckbox_CheckedChanged); + // + // panel1 + // + this.panel1.AutoScroll = true; + this.panel1.Controls.Add(this.overworldPictureBox); + this.panel1.Dock = System.Windows.Forms.DockStyle.Right; + this.panel1.Location = new System.Drawing.Point(283, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(123, 209); + this.panel1.TabIndex = 7; + // + // saveSelected_Button + // + this.saveSelected_Button.Image = global::DSPRE.Properties.Resources.saveButton; + this.saveSelected_Button.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; + this.saveSelected_Button.Location = new System.Drawing.Point(16, 174); + this.saveSelected_Button.Name = "saveSelected_Button"; + this.saveSelected_Button.Size = new System.Drawing.Size(121, 23); + this.saveSelected_Button.TabIndex = 8; + this.saveSelected_Button.Text = "Save Selected"; + this.saveSelected_Button.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; + this.saveSelected_Button.UseVisualStyleBackColor = true; + this.saveSelected_Button.Click += new System.EventHandler(this.saveSelected_Button_Click); + // + // SaveAll_Button + // + this.SaveAll_Button.Image = global::DSPRE.Properties.Resources.saveButton; + this.SaveAll_Button.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; + this.SaveAll_Button.Location = new System.Drawing.Point(148, 174); + this.SaveAll_Button.Name = "SaveAll_Button"; + this.SaveAll_Button.Size = new System.Drawing.Size(121, 23); + this.SaveAll_Button.TabIndex = 9; + this.SaveAll_Button.Text = "Save All"; + this.SaveAll_Button.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; + this.SaveAll_Button.UseVisualStyleBackColor = true; + this.SaveAll_Button.Click += new System.EventHandler(this.SaveAll_Button_Click); + // + // BtxEditor + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(406, 209); + this.Controls.Add(this.SaveAll_Button); + this.Controls.Add(this.saveSelected_Button); + this.Controls.Add(this.panel1); + this.Controls.Add(this.shinyCheckbox); + this.Controls.Add(this.importImagePng); + this.Controls.Add(this.exportImagePng); + this.Controls.Add(this.showBtxFileButton); + this.Controls.Add(this.overworldList); + this.Controls.Add(this.label1); + this.MaximumSize = new System.Drawing.Size(422, 248); + this.MinimumSize = new System.Drawing.Size(422, 248); + this.Name = "BtxEditor"; + this.ShowIcon = false; + this.Text = "Overworld (BTX) Editor"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.BtxEditor_FormClosing); + ((System.ComponentModel.ISupportInitialize)(this.overworldPictureBox)).EndInit(); + this.panel1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox overworldList; + private System.Windows.Forms.PictureBox overworldPictureBox; + private System.Windows.Forms.Button showBtxFileButton; + private System.Windows.Forms.Button exportImagePng; + private System.Windows.Forms.Button importImagePng; + private System.Windows.Forms.CheckBox shinyCheckbox; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Button saveSelected_Button; + private System.Windows.Forms.Button SaveAll_Button; + } +} \ No newline at end of file diff --git a/DS_Map/Editors/BtxEditor/BtxEditor.cs b/DS_Map/Editors/BtxEditor/BtxEditor.cs new file mode 100644 index 0000000..4aa714e --- /dev/null +++ b/DS_Map/Editors/BtxEditor/BtxEditor.cs @@ -0,0 +1,230 @@ +using DSPRE.LibNDSFormats; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Windows.Forms; +using static DSPRE.RomInfo; + +namespace DSPRE.Editors.BtxEditor +{ + public partial class BtxEditor : Form + { + private Bitmap bm; + private byte[] BTXFile; + + // Track modified BTX files + private Dictionary modifiedBTXFiles = new Dictionary(); + + public BtxEditor() + { + RomInfo.SetOWtable(); + RomInfo.Set3DOverworldsDict(); + RomInfo.ReadOWTable(); + InitializeComponent(); + overworldList.Items.Clear(); + + foreach (ushort key in RomInfo.OverworldTable.Keys) + { + overworldList.Items.Add("OW Entry " + key); + } + + this.FormClosing += BtxEditor_FormClosing; + } + + private void overworldList_SelectedIndexChanged(object sender, EventArgs e) + { + int selection = overworldList.SelectedIndex; + if (selection < 0) + { + return; + } + + showBtxFileButton.Enabled = true; + exportImagePng.Enabled = true; + importImagePng.Enabled = true; + + ushort overlayTableEntryID = (ushort)RomInfo.OverworldTable.Keys.ElementAt(selection); + uint spriteID = RomInfo.OverworldTable[overlayTableEntryID].spriteID; + string path = RomInfo.gameDirs[DirNames.OWSprites].unpackedDir + "\\" + spriteID.ToString("D4"); + + if (modifiedBTXFiles.TryGetValue(overlayTableEntryID, out byte[] modifiedData)) + { + BTXFile = modifiedData; + } + else if (File.Exists(path)) + { + BTXFile = File.ReadAllBytes(path); + } + else + { + overworldPictureBox.Image = (Bitmap)Properties.Resources.ResourceManager.GetObject("overworldUnreadable"); + return; + } + + bm = BTX0.Read(BTXFile); + if (bm != null) + { + shinyCheckbox.Enabled = (BTX0.PaletteSize == 64 && BTX0.PaletteCount == 2); + + overworldPictureBox.Width = bm.Width; + overworldPictureBox.Height = bm.Height; + overworldPictureBox.Image = bm; + } + else + { + MessageBox.Show("This file is not supported.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); + } + } + + private void shinyCheckbox_CheckedChanged(object sender, EventArgs e) + { + if (shinyCheckbox.Enabled) + { + BTX0.PaletteIndex = shinyCheckbox.Checked ? 1u : 0u; + bm = BTX0.Read(BTXFile); + overworldPictureBox.Image = bm; + } + } + + private void exportImagePng_Click(object sender, EventArgs e) + { + SaveFileDialog saveFileDialog = new SaveFileDialog(); + saveFileDialog.Title = "Save As"; + saveFileDialog.Filter = "PNG (*.png)|*.png"; + if (saveFileDialog.ShowDialog() == DialogResult.OK) + { + bm.Save(saveFileDialog.FileName); + } + } + + private void importImagePng_Click(object sender, EventArgs e) + { + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.Title = "Open"; + openFileDialog.Filter = "PNG (*.png)|*.png"; + if (openFileDialog.ShowDialog() != DialogResult.OK) + { + return; + } + + Bitmap bitmap = new Bitmap(openFileDialog.FileName); + if (bm.Width == bitmap.Width && bm.Height == bitmap.Height) + { + if (GetColorCount(bitmap) <= BTX0.ColorCount) + { + BTXFile = BTX0.Write(BTXFile, bitmap); + bm = BTX0.Read(BTXFile); + overworldPictureBox.Image = bm; + + ushort overlayTableEntryID = (ushort)RomInfo.OverworldTable.Keys.ElementAt(overworldList.SelectedIndex); + modifiedBTXFiles[overlayTableEntryID] = BTXFile; + + MessageBox.Show($"OW Entry {overlayTableEntryID} marked as modified. Use Save Selected or Save All to write it.", "Pending Save", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + else + { + MessageBox.Show("Too many colors!\nBTX: " + BTX0.ColorCount + "\nPNG: " + GetColorCount(bitmap), "Information", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); + } + } + else + { + MessageBox.Show("Not the same size!\nBTX: " + bm.Width + "x" + bm.Height + "\nPNG: " + bitmap.Width + "x" + bitmap.Height, "Information", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); + } + } + + private uint GetColorCount(Bitmap temp) + { + HashSet hashSet = new HashSet(); + for (int y = 0; y < temp.Height; y++) + { + for (int x = 0; x < temp.Width; x++) + { + hashSet.Add(temp.GetPixel(x, y)); + } + } + return (uint)hashSet.Count; + } + + private void showBtxFileButton_Click(object sender, EventArgs e) + { + ushort overlayTableEntryID = (ushort)RomInfo.OverworldTable.Keys.ElementAt(overworldList.SelectedIndex); + uint spriteID = RomInfo.OverworldTable[overlayTableEntryID].spriteID; + string path = RomInfo.gameDirs[DirNames.OWSprites].unpackedDir + "\\" + spriteID.ToString("D4"); + Helpers.ExplorerSelect(path); + } + + private void SaveAll_Button_Click(object sender, EventArgs e) + { + int savedCount = 0; + foreach (var kvp in modifiedBTXFiles.ToList()) + { + ushort entryID = kvp.Key; + byte[] data = kvp.Value; + uint spriteID = RomInfo.OverworldTable[entryID].spriteID; + string path = Path.Combine(RomInfo.gameDirs[DirNames.OWSprites].unpackedDir, spriteID.ToString("D4")); + + try + { + File.WriteAllBytes(path, data); + modifiedBTXFiles.Remove(entryID); + savedCount++; + } + catch (Exception ex) + { + MessageBox.Show($"Failed to save OW Entry {entryID}: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + MessageBox.Show($"Saved {savedCount} modified BTX file(s).", "Batch Save Complete", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + private void saveSelected_Button_Click(object sender, EventArgs e) + { + if (overworldList.SelectedIndex < 0) return; + + ushort overlayTableEntryID = (ushort)RomInfo.OverworldTable.Keys.ElementAt(overworldList.SelectedIndex); + + if (!modifiedBTXFiles.TryGetValue(overlayTableEntryID, out byte[] btxData)) + { + MessageBox.Show("No modification to save for the selected OW Entry.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + uint spriteID = RomInfo.OverworldTable[overlayTableEntryID].spriteID; + string path = Path.Combine(RomInfo.gameDirs[DirNames.OWSprites].unpackedDir, spriteID.ToString("D4")); + + try + { + File.WriteAllBytes(path, btxData); + modifiedBTXFiles.Remove(overlayTableEntryID); + MessageBox.Show($"Saved OW Entry {overlayTableEntryID} successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show($"Failed to save OW Entry {overlayTableEntryID}: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + private bool isConfirmingExit = false; + + private void BtxEditor_FormClosing(object sender, FormClosingEventArgs e) + { + if (modifiedBTXFiles.Count == 0 || isConfirmingExit || e.Cancel) + return; + + BtxExitConfirmation dialog = new BtxExitConfirmation(modifiedBTXFiles, RomInfo.OverworldTable); + DialogResult result = dialog.ShowDialog(); + + if (result == DialogResult.No) + { + e.Cancel = true; + } else + { + isConfirmingExit = true; + } + + } + + } +} diff --git a/DS_Map/Editors/BtxEditor/BtxEditor.resx b/DS_Map/Editors/BtxEditor/BtxEditor.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/DS_Map/Editors/BtxEditor/BtxEditor.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/DS_Map/Editors/BtxEditor/BtxExitConfirmation.Designer.cs b/DS_Map/Editors/BtxEditor/BtxExitConfirmation.Designer.cs new file mode 100644 index 0000000..f0a77f4 --- /dev/null +++ b/DS_Map/Editors/BtxEditor/BtxExitConfirmation.Designer.cs @@ -0,0 +1,178 @@ +namespace DSPRE.Editors.BtxEditor +{ + partial class BtxExitConfirmation + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.exitWithoutSavingButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.unsavedFileCountLabel = new System.Windows.Forms.Label(); + this.modifiedOverworldsDetails = new System.Windows.Forms.ListBox(); + this.beforeSpriteBox = new System.Windows.Forms.PictureBox(); + this.afterSpriteBox = new System.Windows.Forms.PictureBox(); + this.pictureBox4 = new System.Windows.Forms.PictureBox(); + this.panel1 = new System.Windows.Forms.Panel(); + this.panel2 = new System.Windows.Forms.Panel(); + ((System.ComponentModel.ISupportInitialize)(this.beforeSpriteBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.afterSpriteBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).BeginInit(); + this.panel1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.SuspendLayout(); + // + // exitWithoutSavingButton + // + this.exitWithoutSavingButton.Anchor = System.Windows.Forms.AnchorStyles.None; + this.exitWithoutSavingButton.DialogResult = System.Windows.Forms.DialogResult.Yes; + this.exitWithoutSavingButton.Location = new System.Drawing.Point(12, 165); + this.exitWithoutSavingButton.Name = "exitWithoutSavingButton"; + this.exitWithoutSavingButton.Size = new System.Drawing.Size(112, 43); + this.exitWithoutSavingButton.TabIndex = 0; + this.exitWithoutSavingButton.Text = "Exit Without Saving"; + this.exitWithoutSavingButton.UseVisualStyleBackColor = true; + // + // cancelButton + // + this.cancelButton.Anchor = System.Windows.Forms.AnchorStyles.None; + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.No; + this.cancelButton.Location = new System.Drawing.Point(141, 165); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(112, 43); + this.cancelButton.TabIndex = 1; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // unsavedFileCountLabel + // + this.unsavedFileCountLabel.AutoSize = true; + this.unsavedFileCountLabel.Location = new System.Drawing.Point(12, 9); + this.unsavedFileCountLabel.Name = "unsavedFileCountLabel"; + this.unsavedFileCountLabel.Size = new System.Drawing.Size(35, 13); + this.unsavedFileCountLabel.TabIndex = 2; + this.unsavedFileCountLabel.Text = "label1"; + // + // modifiedOverworldsDetails + // + this.modifiedOverworldsDetails.FormattingEnabled = true; + this.modifiedOverworldsDetails.Location = new System.Drawing.Point(12, 25); + this.modifiedOverworldsDetails.Name = "modifiedOverworldsDetails"; + this.modifiedOverworldsDetails.Size = new System.Drawing.Size(241, 134); + this.modifiedOverworldsDetails.TabIndex = 3; + this.modifiedOverworldsDetails.SelectedIndexChanged += new System.EventHandler(this.modifiedOverworldsDetails_SelectedIndexChanged); + // + // beforeSpriteBox + // + this.beforeSpriteBox.Location = new System.Drawing.Point(0, 0); + this.beforeSpriteBox.Name = "beforeSpriteBox"; + this.beforeSpriteBox.Size = new System.Drawing.Size(48, 190); + this.beforeSpriteBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; + this.beforeSpriteBox.TabIndex = 4; + this.beforeSpriteBox.TabStop = false; + // + // afterSpriteBox + // + this.afterSpriteBox.Location = new System.Drawing.Point(0, 0); + this.afterSpriteBox.Name = "afterSpriteBox"; + this.afterSpriteBox.Size = new System.Drawing.Size(48, 190); + this.afterSpriteBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; + this.afterSpriteBox.TabIndex = 5; + this.afterSpriteBox.TabStop = false; + // + // pictureBox4 + // + this.pictureBox4.BackgroundImage = global::DSPRE.Properties.Resources.arrowright; + this.pictureBox4.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + this.pictureBox4.InitialImage = global::DSPRE.Properties.Resources.arrowright; + this.pictureBox4.Location = new System.Drawing.Point(327, 98); + this.pictureBox4.Name = "pictureBox4"; + this.pictureBox4.Size = new System.Drawing.Size(29, 28); + this.pictureBox4.TabIndex = 6; + this.pictureBox4.TabStop = false; + // + // panel1 + // + this.panel1.AutoScroll = true; + this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panel1.Controls.Add(this.beforeSpriteBox); + this.panel1.Location = new System.Drawing.Point(272, 12); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(51, 196); + this.panel1.TabIndex = 7; + this.panel1.Scroll += new System.Windows.Forms.ScrollEventHandler(this.panel1_Scroll); + // + // panel2 + // + this.panel2.AutoScroll = true; + this.panel2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panel2.Controls.Add(this.afterSpriteBox); + this.panel2.Location = new System.Drawing.Point(362, 12); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(51, 196); + this.panel2.TabIndex = 8; + this.panel2.Scroll += new System.Windows.Forms.ScrollEventHandler(this.panel2_Scroll); + // + // BtxExitConfirmation + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(425, 243); + this.ControlBox = false; + this.Controls.Add(this.panel2); + this.Controls.Add(this.panel1); + this.Controls.Add(this.pictureBox4); + this.Controls.Add(this.modifiedOverworldsDetails); + this.Controls.Add(this.unsavedFileCountLabel); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.exitWithoutSavingButton); + this.MaximumSize = new System.Drawing.Size(441, 282); + this.MinimumSize = new System.Drawing.Size(441, 259); + this.Name = "BtxExitConfirmation"; + this.ShowIcon = false; + this.Text = "Unsaved modifications"; + ((System.ComponentModel.ISupportInitialize)(this.beforeSpriteBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.afterSpriteBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).EndInit(); + this.panel1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button exitWithoutSavingButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Label unsavedFileCountLabel; + private System.Windows.Forms.ListBox modifiedOverworldsDetails; + private System.Windows.Forms.PictureBox beforeSpriteBox; + private System.Windows.Forms.PictureBox afterSpriteBox; + private System.Windows.Forms.PictureBox pictureBox4; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Panel panel2; + } +} \ No newline at end of file diff --git a/DS_Map/Editors/BtxEditor/BtxExitConfirmation.cs b/DS_Map/Editors/BtxEditor/BtxExitConfirmation.cs new file mode 100644 index 0000000..c8ef7ea --- /dev/null +++ b/DS_Map/Editors/BtxEditor/BtxExitConfirmation.cs @@ -0,0 +1,109 @@ +using DSPRE.LibNDSFormats; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; +using System.Windows.Forms; +using static DSPRE.RomInfo; +using Panel = System.Windows.Forms.Panel; + +namespace DSPRE.Editors.BtxEditor +{ + public partial class BtxExitConfirmation : Form + { + private SortedDictionary _overworldList; + private Dictionary _modifiedBtx; + + public BtxExitConfirmation(Dictionary modifiedBtx, SortedDictionary overworldList) + { + InitializeComponent(); + unsavedFileCountLabel.Text = $"{modifiedBtx.Count} BTX file(s) unsaved"; + _overworldList = overworldList; + _modifiedBtx = modifiedBtx; + + foreach (var entryID in modifiedBtx.Keys) + { + uint spriteID = RomInfo.OverworldTable[entryID].spriteID; + modifiedOverworldsDetails.Items.Add($"OW Entry {entryID} → Sprite {spriteID:D4}"); + } + + panel1.MouseWheel += Panel_MouseWheel; + panel2.MouseWheel += Panel_MouseWheel; + } + + private void modifiedOverworldsDetails_SelectedIndexChanged(object sender, EventArgs e) + { + var selection = modifiedOverworldsDetails.SelectedIndex; + + if (selection == -1) return; + var selectedObj = _modifiedBtx.ElementAt(selection); + Bitmap newSprite = BTX0.Read(selectedObj.Value); + afterSpriteBox.Image = newSprite; + afterSpriteBox.Width = newSprite.Width; + afterSpriteBox.Height = newSprite.Height; + + ushort overlayTableEntryID = (ushort)_overworldList.ElementAt(selectedObj.Key).Key; + uint spriteID = _overworldList[overlayTableEntryID].spriteID; + string path = RomInfo.gameDirs[DirNames.OWSprites].unpackedDir + "\\" + spriteID.ToString("D4"); + + if (File.Exists(path)) + { + Bitmap oldSprite = BTX0.Read(File.ReadAllBytes(path)); + beforeSpriteBox.Image = oldSprite; + beforeSpriteBox.Width = oldSprite.Width; + beforeSpriteBox.Height = oldSprite.Height; + } + + } + + private bool syncing = false; + + private void panel1_Scroll(object sender, ScrollEventArgs e) + { + if (syncing) return; + syncing = true; + panel2.AutoScrollPosition = new Point( + panel2.AutoScrollPosition.X, + panel1.VerticalScroll.Value + ); + syncing = false; + } + + private void panel2_Scroll(object sender, ScrollEventArgs e) + { + if (syncing) return; + syncing = true; + panel1.AutoScrollPosition = new Point( + panel1.AutoScrollPosition.X, + panel2.VerticalScroll.Value + ); + syncing = false; + } + + + private void Panel_MouseWheel(object sender, MouseEventArgs e) + { + var source = sender as Panel; + var target = (source == panel1) ? panel2 : panel1; + + int scrollDelta = e.Delta; // Usually 120 or -120 + int newValue = source.VerticalScroll.Value - scrollDelta; + + // Clamp the new scroll value within limits + newValue = Math.Max(source.VerticalScroll.Minimum, + Math.Min(source.VerticalScroll.Maximum - source.Height, newValue)); + + source.AutoScrollPosition = new Point(0, newValue); + target.AutoScrollPosition = new Point(0, newValue); + } + + + + } +} diff --git a/DS_Map/Editors/BtxEditor/BtxExitConfirmation.resx b/DS_Map/Editors/BtxEditor/BtxExitConfirmation.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/DS_Map/Editors/BtxEditor/BtxExitConfirmation.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/DS_Map/LibNDSFormats/BTX0.cs b/DS_Map/LibNDSFormats/BTX0.cs new file mode 100644 index 0000000..f6c5780 --- /dev/null +++ b/DS_Map/LibNDSFormats/BTX0.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DSPRE.LibNDSFormats +{ + internal class BTX0 + { + public static uint PaletteIndex; + + public static uint PaletteCount; + + public static uint PaletteSize; + + public static uint ColorCount; + + public static uint ImageOffset; + + public static uint PaletteOffset; + + public static uint ImageWidth; + + public static uint ImageHeight; + public static Bitmap Read(byte[] BTXFile) + { + if (BitConverter.ToUInt32(BTXFile, 0) != 811095106) + { + return null; + } + uint num = BitConverter.ToUInt32(BTXFile, 16); + if (BitConverter.ToUInt32(BTXFile, (int)num) != 811091284) + { + return null; + } + uint num2 = num + BitConverter.ToUInt16(BTXFile, (int)(num + 14)); + uint num3 = (ImageOffset = num + BitConverter.ToUInt32(BTXFile, (int)(num + 20))); + uint num4 = BitConverter.ToUInt32(BTXFile, (int)(num + 48)) << 3; + uint num5 = num + BitConverter.ToUInt32(BTXFile, (int)(num + 52)); + uint num6 = (PaletteOffset = num + BitConverter.ToUInt32(BTXFile, (int)(num + 56))); + uint num7 = BTXFile[num2 + 1]; + uint num8 = BitConverter.ToUInt16(BTXFile, (int)(num2 + 12 + num7 * 4 + 6)); + uint num9 = (uint)(8 << (((int)num8 >> 4) & 7)); + uint num10 = (num8 >> 10) & 7; + uint num11 = (PaletteCount = BTXFile[num5 + 1]); + PaletteSize = num4; + if (num10 == 3) + { + Color[] array = new Color[num4 / num11 / 2]; + if (num4 < 64 && num11 >= 2) + { + array = new Color[(BTXFile.Length - num6) / 2]; + } + ColorCount = (uint)array.Length; + for (int i = 0; i < array.Length; i++) + { + ushort num12 = BitConverter.ToUInt16(BTXFile, (int)(num6 + PaletteIndex * (ColorCount * 2)) + i * 2); + uint red = (uint)((num12 & 0x1F) << 3); + uint green = (uint)(num12 & 0x3E0) >> 2; + uint blue = (uint)(num12 & 0x7C00) >> 7; + array[i] = Color.FromArgb(255, (int)red, (int)green, (int)blue); + } + ImageWidth = num9; + ImageHeight = (num6 - num3) * 2 / num9; + Bitmap bitmap = new Bitmap((int)ImageWidth, (int)ImageHeight); + uint num13 = 0u; + uint num14 = 0u; + for (int j = (int)num3; j < num6; j++) + { + uint num15 = BTXFile[j]; + uint[] array2 = new uint[2] + { + num15 & 0xF, + num15 >> 4 + }; + for (int k = 0; k < array2.Length; k++) + { + bitmap.SetPixel((int)num13, (int)num14, array[array2[k]]); + num13++; + } + if (num13 >= num9) + { + num13 = 0u; + num14++; + } + } + return bitmap; + } + return null; + } + + public static byte[] Write(byte[] BTXFile, Bitmap bm) + { + HashSet hashSet = new HashSet(); + uint num = 0u; + uint num2 = 0u; + for (int i = 0; i < bm.Width * bm.Height; i++) + { + hashSet.Add(bm.GetPixel((int)num, (int)num2)); + num++; + if (num >= bm.Width) + { + num = 0u; + num2++; + } + } + Color[] array = hashSet.ToArray(); + num = 0u; + num2 = 0u; + for (int j = (int)ImageOffset; j < PaletteOffset; j++) + { + Color pixel = bm.GetPixel((int)num, (int)num2); + num++; + uint num3 = 0u; + for (int k = 0; k < array.Length; k++) + { + if (array[k] == pixel) + { + num3 = (uint)k; + break; + } + } + pixel = bm.GetPixel((int)num, (int)num2); + num++; + for (int l = 0; l < array.Length; l++) + { + if (array[l] == pixel) + { + num3 += (uint)(l << 4); + break; + } + } + BTXFile[j] = (byte)num3; + if (num >= ImageWidth) + { + num = 0u; + num2++; + } + } + for (int m = 0; m < array.Length; m++) + { + uint num4 = (uint)Math.Round((double)(int)array[m].R / 8.0); + uint num5 = (uint)Math.Round((double)(int)array[m].G / 8.0); + uint num6 = (uint)Math.Round((double)(int)array[m].B / 8.0); + if (num4 > 31) + { + num4 = 31u; + } + if (num5 > 31) + { + num5 = 31u; + } + if (num6 > 31) + { + num6 = 31u; + } + uint num7 = num4 + (num5 << 5) + (num6 << 10); + BTXFile[PaletteOffset + PaletteIndex * (ColorCount * 2) + m * 2] = (byte)num7; + BTXFile[PaletteOffset + PaletteIndex * (ColorCount * 2) + m * 2 + 1] = (byte)(num7 >> 8); + } + return BTXFile; + } + } + +} diff --git a/DS_Map/Main Window.Designer.cs b/DS_Map/Main Window.Designer.cs index 17f8123..b574e09 100644 --- a/DS_Map/Main Window.Designer.cs +++ b/DS_Map/Main Window.Designer.cs @@ -825,7 +825,8 @@ this.overlayEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.spawnEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.moveDataEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.flyWarpEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.overworldEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.flyWarpEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.statusLabel = new System.Windows.Forms.ToolStripStatusLabel(); @@ -10550,7 +10551,8 @@ this.spawnEditorToolStripMenuItem, this.moveDataEditorToolStripMenuItem, this.flyWarpEditorToolStripMenuItem, - this.itemEditorToolStripMenuItem}); + this.itemEditorToolStripMenuItem, + this.overworldEditorToolStripMenuItem}); this.otherEditorsToolStripMenuItem.Enabled = false; this.otherEditorsToolStripMenuItem.Name = "otherEditorsToolStripMenuItem"; this.otherEditorsToolStripMenuItem.Size = new System.Drawing.Size(88, 20); @@ -10590,7 +10592,15 @@ this.flyWarpEditorToolStripMenuItem.Size = new System.Drawing.Size(180, 22); this.flyWarpEditorToolStripMenuItem.Text = "Fly Warp Editor"; this.flyWarpEditorToolStripMenuItem.Click += new System.EventHandler(this.flyWarpEditorToolStripMenuItem_Click); + + // + // overworldEditorToolStripMenuItem // + this.overworldEditorToolStripMenuItem.Name = "overworldEditorToolStripMenuItem"; + this.overworldEditorToolStripMenuItem.Size = new System.Drawing.Size(174, 22); + this.overworldEditorToolStripMenuItem.Text = "Overworld Editor"; + this.overworldEditorToolStripMenuItem.Click += new System.EventHandler(this.overworldEditorToolStripMenuItem_Click); + // aboutToolStripMenuItem1 // this.aboutToolStripMenuItem1.Name = "aboutToolStripMenuItem1"; @@ -12130,6 +12140,7 @@ private System.Windows.Forms.TrackBar transparencyBar; private System.Windows.Forms.ToolStripMenuItem addressHelperToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem exportScriptDatabaseJSONToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem overworldEditorToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem generateCSVToolStripMenuItem; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Panel panel1; diff --git a/DS_Map/Main Window.cs b/DS_Map/Main Window.cs index 7f3d202..30e145d 100644 --- a/DS_Map/Main Window.cs +++ b/DS_Map/Main Window.cs @@ -37,6 +37,7 @@ using static DSPRE.ROMFiles.SpeciesFile; using System.Reflection; using System.ComponentModel; using DSPRE.Editors; +using DSPRE.Editors.BtxEditor; namespace DSPRE { @@ -10182,6 +10183,13 @@ namespace DSPRE { form.Show(); } + + private void overworldEditorToolStripMenuItem_Click(object sender, EventArgs e) + { + + BtxEditor form = new BtxEditor(); + form.Show(); + } private void exportScriptDatabaseJSONToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox.Show("Not implemented yet");