From 1dd28e90c52f431e3dfd1a158da24427d5ca223d Mon Sep 17 00:00:00 2001 From: Mixone-FinallyHere Date: Mon, 9 Jun 2025 15:28:18 +0200 Subject: [PATCH] Restructured script stuff so it can be released for 1.13 --- DS_Map/DSPRE.csproj | 14 +- DS_Map/Editors/ScriptEditor.Designer.cs | 261 +++- DS_Map/Editors/ScriptEditor.cs | 860 ++++++----- DS_Map/Main Window.cs | 14 +- DS_Map/PatchToolboxDialog.cs | 69 +- .../LevelScriptTrigger.cs | 0 .../MapScreenLoadTrigger.cs | 0 DS_Map/{Script => ROMFiles}/ScriptAction.cs | 0 DS_Map/ROMFiles/ScriptActionContainer.cs | 16 + DS_Map/{Script => ROMFiles}/ScriptCommand.cs | 108 +- DS_Map/ROMFiles/ScriptCommandContainer.cs | 29 + DS_Map/ROMFiles/ScriptFile.cs | 1370 +++++++++++------ .../{Script => ROMFiles}/ScriptReference.cs | 0 .../VariableValueTrigger.cs | 0 14 files changed, 1804 insertions(+), 937 deletions(-) rename DS_Map/{Script => ROMFiles}/LevelScriptTrigger.cs (100%) rename DS_Map/{Script => ROMFiles}/MapScreenLoadTrigger.cs (100%) rename DS_Map/{Script => ROMFiles}/ScriptAction.cs (100%) create mode 100644 DS_Map/ROMFiles/ScriptActionContainer.cs rename DS_Map/{Script => ROMFiles}/ScriptCommand.cs (81%) create mode 100644 DS_Map/ROMFiles/ScriptCommandContainer.cs rename DS_Map/{Script => ROMFiles}/ScriptReference.cs (100%) rename DS_Map/{Script => ROMFiles}/VariableValueTrigger.cs (100%) diff --git a/DS_Map/DSPRE.csproj b/DS_Map/DSPRE.csproj index d5cd73b..da19c95 100644 --- a/DS_Map/DSPRE.csproj +++ b/DS_Map/DSPRE.csproj @@ -280,14 +280,16 @@ + + - - - + + + - + @@ -303,7 +305,7 @@ BuildingEditor.cs - + Form @@ -312,7 +314,7 @@ ScriptTooltip.cs - + Form diff --git a/DS_Map/Editors/ScriptEditor.Designer.cs b/DS_Map/Editors/ScriptEditor.Designer.cs index 1cd0f69..535d85d 100644 --- a/DS_Map/Editors/ScriptEditor.Designer.cs +++ b/DS_Map/Editors/ScriptEditor.Designer.cs @@ -31,12 +31,28 @@ namespace DSPRE.Editors { this.selectScriptFileComboBox = new System.Windows.Forms.ComboBox(); this.label5 = new System.Windows.Forms.Label(); + this.scriptEditorTabControl = new System.Windows.Forms.TabControl(); + this.scriptsTabPage = new System.Windows.Forms.TabPage(); this.PanelSearchScripts = new System.Windows.Forms.Panel(); this.BtnNextFindScript = new System.Windows.Forms.Button(); this.BtnPrevFindScript = new System.Windows.Forms.Button(); this.BtnCloseFindScript = new System.Windows.Forms.Button(); this.panelFindScriptTextBox = new System.Windows.Forms.TextBox(); this.scintillaScriptsPanel = new System.Windows.Forms.Panel(); + this.functionsTabPage = new System.Windows.Forms.TabPage(); + this.PanelSearchFunctions = new System.Windows.Forms.Panel(); + this.BtnNextFindFunc = new System.Windows.Forms.Button(); + this.BtnPrevFindFunc = new System.Windows.Forms.Button(); + this.BtnCloseFindFunc = new System.Windows.Forms.Button(); + this.panelFindFunctionTextBox = new System.Windows.Forms.TextBox(); + this.scintillaFunctionsPanel = new System.Windows.Forms.Panel(); + this.actionsTabPage = new System.Windows.Forms.TabPage(); + this.PanelSearchActions = new System.Windows.Forms.Panel(); + this.BtnNextFindActions = new System.Windows.Forms.Button(); + this.BtnPrevFindActions = new System.Windows.Forms.Button(); + this.BtnCloseFindActions = new System.Windows.Forms.Button(); + this.panelFindActionTextBox = new System.Windows.Forms.TextBox(); + this.scintillaActionsPanel = new System.Windows.Forms.Panel(); this.addScriptFileButton = new System.Windows.Forms.Button(); this.removeScriptFileButton = new System.Windows.Forms.Button(); this.saveScriptFileButton = new System.Windows.Forms.Button(); @@ -72,8 +88,13 @@ namespace DSPRE.Editors this.scriptEditorNumberFormatHex = new System.Windows.Forms.RadioButton(); this.viewLevelScriptButton = new System.Windows.Forms.Button(); this.locateCurrentScriptFile = new System.Windows.Forms.Button(); + this.scriptEditorTabControl.SuspendLayout(); + this.scriptsTabPage.SuspendLayout(); this.PanelSearchScripts.SuspendLayout(); - this.scintillaScriptsPanel.SuspendLayout(); + this.functionsTabPage.SuspendLayout(); + this.PanelSearchFunctions.SuspendLayout(); + this.actionsTabPage.SuspendLayout(); + this.PanelSearchActions.SuspendLayout(); this.groupBox8.SuspendLayout(); this.groupBox24.SuspendLayout(); this.ScriptNavigatorTabControl.SuspendLayout(); @@ -102,6 +123,31 @@ namespace DSPRE.Editors this.label5.TabIndex = 1; this.label5.Text = "Script File"; // + // scriptEditorTabControl + // + this.scriptEditorTabControl.Controls.Add(this.scriptsTabPage); + this.scriptEditorTabControl.Controls.Add(this.functionsTabPage); + this.scriptEditorTabControl.Controls.Add(this.actionsTabPage); + this.scriptEditorTabControl.Location = new System.Drawing.Point(481, 22); + this.scriptEditorTabControl.Name = "scriptEditorTabControl"; + this.scriptEditorTabControl.SelectedIndex = 0; + this.scriptEditorTabControl.Size = new System.Drawing.Size(692, 591); + this.scriptEditorTabControl.TabIndex = 18; + this.scriptEditorTabControl.SelectedIndexChanged += new System.EventHandler(this.scriptEditorTabControl_TabIndexChanged); + // + // scriptsTabPage + // + this.scriptsTabPage.Controls.Add(this.PanelSearchScripts); + this.scriptsTabPage.Controls.Add(this.scintillaScriptsPanel); + this.scriptsTabPage.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.scriptsTabPage.Location = new System.Drawing.Point(4, 22); + this.scriptsTabPage.Name = "scriptsTabPage"; + this.scriptsTabPage.Padding = new System.Windows.Forms.Padding(3); + this.scriptsTabPage.Size = new System.Drawing.Size(684, 565); + this.scriptsTabPage.TabIndex = 0; + this.scriptsTabPage.Text = "Scripts"; + this.scriptsTabPage.UseVisualStyleBackColor = true; + // // PanelSearchScripts // this.PanelSearchScripts.BackColor = System.Drawing.Color.White; @@ -110,7 +156,7 @@ namespace DSPRE.Editors this.PanelSearchScripts.Controls.Add(this.BtnPrevFindScript); this.PanelSearchScripts.Controls.Add(this.BtnCloseFindScript); this.PanelSearchScripts.Controls.Add(this.panelFindScriptTextBox); - this.PanelSearchScripts.Location = new System.Drawing.Point(383, 3); + this.PanelSearchScripts.Location = new System.Drawing.Point(386, 3); this.PanelSearchScripts.Name = "PanelSearchScripts"; this.PanelSearchScripts.Size = new System.Drawing.Size(292, 40); this.PanelSearchScripts.TabIndex = 14; @@ -172,13 +218,193 @@ namespace DSPRE.Editors // // scintillaScriptsPanel // - this.scintillaScriptsPanel.Controls.Add(this.PanelSearchScripts); - this.scintillaScriptsPanel.Location = new System.Drawing.Point(483, 50); + this.scintillaScriptsPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.scintillaScriptsPanel.Location = new System.Drawing.Point(3, 3); this.scintillaScriptsPanel.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.scintillaScriptsPanel.Name = "scintillaScriptsPanel"; this.scintillaScriptsPanel.Size = new System.Drawing.Size(678, 559); this.scintillaScriptsPanel.TabIndex = 19; // + // functionsTabPage + // + this.functionsTabPage.Controls.Add(this.PanelSearchFunctions); + this.functionsTabPage.Controls.Add(this.scintillaFunctionsPanel); + this.functionsTabPage.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.functionsTabPage.Location = new System.Drawing.Point(4, 22); + this.functionsTabPage.Name = "functionsTabPage"; + this.functionsTabPage.Padding = new System.Windows.Forms.Padding(3); + this.functionsTabPage.Size = new System.Drawing.Size(684, 565); + this.functionsTabPage.TabIndex = 1; + this.functionsTabPage.Text = "Functions"; + this.functionsTabPage.UseVisualStyleBackColor = true; + // + // PanelSearchFunctions + // + this.PanelSearchFunctions.BackColor = System.Drawing.Color.White; + this.PanelSearchFunctions.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.PanelSearchFunctions.Controls.Add(this.BtnNextFindFunc); + this.PanelSearchFunctions.Controls.Add(this.BtnPrevFindFunc); + this.PanelSearchFunctions.Controls.Add(this.BtnCloseFindFunc); + this.PanelSearchFunctions.Controls.Add(this.panelFindFunctionTextBox); + this.PanelSearchFunctions.Location = new System.Drawing.Point(386, 3); + this.PanelSearchFunctions.Name = "PanelSearchFunctions"; + this.PanelSearchFunctions.Size = new System.Drawing.Size(292, 40); + this.PanelSearchFunctions.TabIndex = 16; + this.PanelSearchFunctions.Visible = false; + // + // BtnNextFindFunc + // + this.BtnNextFindFunc.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnNextFindFunc.ForeColor = System.Drawing.Color.White; + this.BtnNextFindFunc.Image = global::DSPRE.Properties.Resources.arrowdown; + this.BtnNextFindFunc.Location = new System.Drawing.Point(233, 4); + this.BtnNextFindFunc.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnNextFindFunc.Name = "BtnNextFindFunc"; + this.BtnNextFindFunc.Size = new System.Drawing.Size(25, 30); + this.BtnNextFindFunc.TabIndex = 36; + this.BtnNextFindFunc.Tag = "Find next (Enter)"; + this.BtnNextFindFunc.UseVisualStyleBackColor = true; + this.BtnNextFindFunc.Click += new System.EventHandler(this.BtnNextFindFunc_Click); + // + // BtnPrevFindFunc + // + this.BtnPrevFindFunc.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnPrevFindFunc.ForeColor = System.Drawing.Color.White; + this.BtnPrevFindFunc.Image = global::DSPRE.Properties.Resources.arrowup; + this.BtnPrevFindFunc.Location = new System.Drawing.Point(205, 4); + this.BtnPrevFindFunc.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnPrevFindFunc.Name = "BtnPrevFindFunc"; + this.BtnPrevFindFunc.Size = new System.Drawing.Size(25, 30); + this.BtnPrevFindFunc.TabIndex = 35; + this.BtnPrevFindFunc.Tag = "Find previous (Shift+Enter)"; + this.BtnPrevFindFunc.UseVisualStyleBackColor = true; + this.BtnPrevFindFunc.Click += new System.EventHandler(this.BtnPrevFindFunc_Click); + // + // BtnCloseFindFunc + // + this.BtnCloseFindFunc.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnCloseFindFunc.ForeColor = System.Drawing.Color.White; + this.BtnCloseFindFunc.Image = global::DSPRE.Properties.Resources.Cross; + this.BtnCloseFindFunc.Location = new System.Drawing.Point(261, 4); + this.BtnCloseFindFunc.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnCloseFindFunc.Name = "BtnCloseFindFunc"; + this.BtnCloseFindFunc.Size = new System.Drawing.Size(25, 30); + this.BtnCloseFindFunc.TabIndex = 37; + this.BtnCloseFindFunc.Tag = "Close (Esc)"; + this.BtnCloseFindFunc.UseVisualStyleBackColor = true; + this.BtnCloseFindFunc.Click += new System.EventHandler(this.BtnCloseFindFunc_Click); + // + // panelFindFunctionTextBox + // + this.panelFindFunctionTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.panelFindFunctionTextBox.Font = new System.Drawing.Font("Segoe UI", 13.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.panelFindFunctionTextBox.Location = new System.Drawing.Point(10, 6); + this.panelFindFunctionTextBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.panelFindFunctionTextBox.Name = "panelFindFunctionTextBox"; + this.panelFindFunctionTextBox.Size = new System.Drawing.Size(189, 25); + this.panelFindFunctionTextBox.TabIndex = 34; + this.panelFindFunctionTextBox.TextChanged += new System.EventHandler(this.panelFindFunctionTextBox_TextChanged); + this.panelFindFunctionTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.functionTxtFind_KeyDown); + // + // scintillaFunctionsPanel + // + this.scintillaFunctionsPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.scintillaFunctionsPanel.Location = new System.Drawing.Point(3, 3); + this.scintillaFunctionsPanel.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.scintillaFunctionsPanel.Name = "scintillaFunctionsPanel"; + this.scintillaFunctionsPanel.Size = new System.Drawing.Size(678, 559); + this.scintillaFunctionsPanel.TabIndex = 20; + // + // actionsTabPage + // + this.actionsTabPage.Controls.Add(this.PanelSearchActions); + this.actionsTabPage.Controls.Add(this.scintillaActionsPanel); + this.actionsTabPage.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.actionsTabPage.Location = new System.Drawing.Point(4, 22); + this.actionsTabPage.Name = "actionsTabPage"; + this.actionsTabPage.Padding = new System.Windows.Forms.Padding(3); + this.actionsTabPage.Size = new System.Drawing.Size(684, 565); + this.actionsTabPage.TabIndex = 2; + this.actionsTabPage.Text = "Actions"; + this.actionsTabPage.UseVisualStyleBackColor = true; + // + // PanelSearchActions + // + this.PanelSearchActions.BackColor = System.Drawing.Color.White; + this.PanelSearchActions.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.PanelSearchActions.Controls.Add(this.BtnNextFindActions); + this.PanelSearchActions.Controls.Add(this.BtnPrevFindActions); + this.PanelSearchActions.Controls.Add(this.BtnCloseFindActions); + this.PanelSearchActions.Controls.Add(this.panelFindActionTextBox); + this.PanelSearchActions.Location = new System.Drawing.Point(386, 3); + this.PanelSearchActions.Name = "PanelSearchActions"; + this.PanelSearchActions.Size = new System.Drawing.Size(292, 40); + this.PanelSearchActions.TabIndex = 16; + this.PanelSearchActions.Visible = false; + // + // BtnNextFindActions + // + this.BtnNextFindActions.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnNextFindActions.ForeColor = System.Drawing.Color.White; + this.BtnNextFindActions.Image = global::DSPRE.Properties.Resources.arrowdown; + this.BtnNextFindActions.Location = new System.Drawing.Point(233, 4); + this.BtnNextFindActions.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnNextFindActions.Name = "BtnNextFindActions"; + this.BtnNextFindActions.Size = new System.Drawing.Size(25, 30); + this.BtnNextFindActions.TabIndex = 40; + this.BtnNextFindActions.Tag = "Find next (Enter)"; + this.BtnNextFindActions.UseVisualStyleBackColor = true; + this.BtnNextFindActions.Click += new System.EventHandler(this.BtnNextFindActions_Click); + // + // BtnPrevFindActions + // + this.BtnPrevFindActions.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnPrevFindActions.ForeColor = System.Drawing.Color.White; + this.BtnPrevFindActions.Image = global::DSPRE.Properties.Resources.arrowup; + this.BtnPrevFindActions.Location = new System.Drawing.Point(205, 4); + this.BtnPrevFindActions.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnPrevFindActions.Name = "BtnPrevFindActions"; + this.BtnPrevFindActions.Size = new System.Drawing.Size(25, 30); + this.BtnPrevFindActions.TabIndex = 39; + this.BtnPrevFindActions.Tag = "Find previous (Shift+Enter)"; + this.BtnPrevFindActions.UseVisualStyleBackColor = true; + this.BtnPrevFindActions.Click += new System.EventHandler(this.BtnPrevFindActions_Click); + // + // BtnCloseFindActions + // + this.BtnCloseFindActions.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnCloseFindActions.ForeColor = System.Drawing.Color.White; + this.BtnCloseFindActions.Image = global::DSPRE.Properties.Resources.Cross; + this.BtnCloseFindActions.Location = new System.Drawing.Point(261, 4); + this.BtnCloseFindActions.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnCloseFindActions.Name = "BtnCloseFindActions"; + this.BtnCloseFindActions.Size = new System.Drawing.Size(25, 30); + this.BtnCloseFindActions.TabIndex = 41; + this.BtnCloseFindActions.Tag = "Close (Esc)"; + this.BtnCloseFindActions.UseVisualStyleBackColor = true; + this.BtnCloseFindActions.Click += new System.EventHandler(this.BtnCloseFindActions_Click); + // + // panelFindActionTextBox + // + this.panelFindActionTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.panelFindActionTextBox.Font = new System.Drawing.Font("Segoe UI", 13.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.panelFindActionTextBox.Location = new System.Drawing.Point(10, 6); + this.panelFindActionTextBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.panelFindActionTextBox.Name = "panelFindActionTextBox"; + this.panelFindActionTextBox.Size = new System.Drawing.Size(189, 25); + this.panelFindActionTextBox.TabIndex = 38; + this.panelFindActionTextBox.TextChanged += new System.EventHandler(this.panelFindActionTextBox_TextChanged); + this.panelFindActionTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.actionTxtFind_KeyDown); + // + // scintillaActionsPanel + // + this.scintillaActionsPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.scintillaActionsPanel.Location = new System.Drawing.Point(3, 3); + this.scintillaActionsPanel.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.scintillaActionsPanel.Name = "scintillaActionsPanel"; + this.scintillaActionsPanel.Size = new System.Drawing.Size(678, 559); + this.scintillaActionsPanel.TabIndex = 21; + // // addScriptFileButton // this.addScriptFileButton.Image = global::DSPRE.Properties.Resources.addIcon; @@ -582,7 +808,6 @@ namespace DSPRE.Editors // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.scintillaScriptsPanel); this.Controls.Add(this.locateCurrentScriptFile); this.Controls.Add(this.groupBox24); this.Controls.Add(this.viewLevelScriptButton); @@ -600,11 +825,19 @@ namespace DSPRE.Editors this.Controls.Add(this.exportScriptFileButton); this.Controls.Add(this.importScriptFileButton); this.Controls.Add(this.groupBox8); + this.Controls.Add(this.scriptEditorTabControl); this.Name = "ScriptEditor"; this.Size = new System.Drawing.Size(1177, 735); + this.scriptEditorTabControl.ResumeLayout(false); + this.scriptsTabPage.ResumeLayout(false); this.PanelSearchScripts.ResumeLayout(false); this.PanelSearchScripts.PerformLayout(); - this.scintillaScriptsPanel.ResumeLayout(false); + this.functionsTabPage.ResumeLayout(false); + this.PanelSearchFunctions.ResumeLayout(false); + this.PanelSearchFunctions.PerformLayout(); + this.actionsTabPage.ResumeLayout(false); + this.PanelSearchActions.ResumeLayout(false); + this.PanelSearchActions.PerformLayout(); this.groupBox8.ResumeLayout(false); this.groupBox8.PerformLayout(); this.groupBox24.ResumeLayout(false); @@ -625,12 +858,28 @@ namespace DSPRE.Editors public System.Windows.Forms.ComboBox selectScriptFileComboBox; private System.Windows.Forms.Label label5; + public System.Windows.Forms.TabControl scriptEditorTabControl; + private System.Windows.Forms.TabPage scriptsTabPage; private System.Windows.Forms.Panel PanelSearchScripts; private System.Windows.Forms.Button BtnNextFindScript; private System.Windows.Forms.Button BtnPrevFindScript; private System.Windows.Forms.Button BtnCloseFindScript; private System.Windows.Forms.TextBox panelFindScriptTextBox; private System.Windows.Forms.Panel scintillaScriptsPanel; + private System.Windows.Forms.TabPage functionsTabPage; + private System.Windows.Forms.Panel PanelSearchFunctions; + private System.Windows.Forms.Button BtnNextFindFunc; + private System.Windows.Forms.Button BtnPrevFindFunc; + private System.Windows.Forms.Button BtnCloseFindFunc; + private System.Windows.Forms.TextBox panelFindFunctionTextBox; + private System.Windows.Forms.Panel scintillaFunctionsPanel; + private System.Windows.Forms.TabPage actionsTabPage; + private System.Windows.Forms.Panel PanelSearchActions; + private System.Windows.Forms.Button BtnNextFindActions; + private System.Windows.Forms.Button BtnPrevFindActions; + private System.Windows.Forms.Button BtnCloseFindActions; + private System.Windows.Forms.TextBox panelFindActionTextBox; + private System.Windows.Forms.Panel scintillaActionsPanel; private System.Windows.Forms.Button addScriptFileButton; private System.Windows.Forms.Button removeScriptFileButton; private System.Windows.Forms.Button saveScriptFileButton; diff --git a/DS_Map/Editors/ScriptEditor.cs b/DS_Map/Editors/ScriptEditor.cs index 77e3326..4949642 100644 --- a/DS_Map/Editors/ScriptEditor.cs +++ b/DS_Map/Editors/ScriptEditor.cs @@ -9,12 +9,19 @@ using DSPRE.ROMFiles; using ScintillaNET; using ScintillaNET.Utils; using System.Globalization; - -namespace DSPRE.Editors { - public partial class ScriptEditor : UserControl { +namespace DSPRE.Editors +{ + public partial class ScriptEditor : UserControl + { public bool scriptEditorIsReady { get; set; } = false; - private SearchManager scriptSearchManager; private Scintilla ScriptTextArea; + private Scintilla FunctionTextArea; + private Scintilla ActionTextArea; + private SearchManager scriptSearchManager; + private SearchManager functionSearchManager; + private SearchManager actionSearchManager; + private Scintilla currentScintillaEditor; + private SearchManager currentSearchManager; private bool scriptsDirty = false; private bool functionsDirty = false; private bool actionsDirty = false; @@ -26,50 +33,40 @@ namespace DSPRE.Editors { /// the background color of the text area /// private readonly Color BACK_COLOR = Color.FromArgb(0x2A211C); - /// /// default text color of the text area /// private readonly Color FORE_COLOR = Color.FromArgb(0xB7B7B7); - /// /// change this to whatever margin you want the line numbers to show in /// private const int NUMBER_MARGIN = 1; - /// /// change this to whatever margin you want the bookmarks/breakpoints to show in /// private const int BOOKMARK_MARGIN = 2; - private const int BOOKMARK_MARKER = 2; - /// /// change this to whatever margin you want the code folding tree (+/-) to show in /// private const int FOLDING_MARGIN = 3; - /// /// set this true to show circular buttons for code folding (the [+] and [-] buttons on the margin) /// private const bool CODE_FOLDING_CIRCULAR = true; - readonly Point initial_importScriptFileButton_location; readonly Point initial_exportScriptFileButton_location; readonly Point initial_addScriptFileButton_location; readonly Point initial_removeScriptFileButton_location; readonly Point initial_viewLevelScript_location; - readonly Point new_importScriptFileButton_location; readonly Point new_exportScriptFileButton_location; readonly Point new_addScriptFileButton_location; readonly Point new_removeScriptFileButton_location; readonly Point new_viewLevelScript_location; - - public ScriptEditor() { + public ScriptEditor() + { InitializeComponent(); - - //initially, these buttons are off the canvas so they can be interacted with in the designer //they are then moved as needed initial_importScriptFileButton_location = importScriptFileButton.Location; @@ -77,111 +74,132 @@ namespace DSPRE.Editors { initial_addScriptFileButton_location = addScriptFileButton.Location; initial_removeScriptFileButton_location = removeScriptFileButton.Location; initial_viewLevelScript_location = viewLevelScriptButton.Location; - new_importScriptFileButton_location = new Point(164, 22); new_exportScriptFileButton_location = new Point(239, 22); new_addScriptFileButton_location = new Point(314, 22); new_removeScriptFileButton_location = new Point(314, 49); new_viewLevelScript_location = new Point(326, 37); - importScriptFileButton.Enabled = false; exportScriptFileButton.Enabled = false; addScriptFileButton.Enabled = false; removeScriptFileButton.Enabled = false; viewLevelScriptButton.Enabled = false; } - - public void SetupScriptEditor(MainProgram parent, bool force = false) { + public void SetupScriptEditor(MainProgram parent, bool force = false) + { if (scriptEditorIsReady && !force) { return; } scriptEditorIsReady = true; this._parent = parent; SetupScriptEditorTextAreas(); - /* Extract essential NARCs sub-archives*/ Helpers.statusLabelMessage("Setting up Script Editor..."); Update(); - DSUtils.TryUnpackNarcs(new List { RomInfo.DirNames.scripts }); //12 = scripts Narc Dir - populate_selectScriptFileComboBox(0); - UpdateScriptNumberCheckBox((NumberStyles)Properties.Settings.Default.scriptEditorFormatPreference); Helpers.statusLabelMessage(); } - - public void OpenScriptEditor(MainProgram parent, int scriptFileID) { + public void OpenScriptEditor(MainProgram parent, int scriptFileID) + { SetupScriptEditor(parent); + scriptEditorTabControl.SelectedIndex = 0; selectScriptFileComboBox.SelectedIndex = scriptFileID; EditorPanels.mainTabControl.SelectedTab = EditorPanels.scriptEditorTabPage; } - - private void SetupScriptEditorTextAreas() { + private void SetupScriptEditorTextAreas() + { //PREPARE SCRIPT EDITOR KEYWORDS cmdKeyWords = String.Join(" ", RomInfo.ScriptCommandNamesDict.Values) + " " + String.Join(" ", ScriptDatabase.movementsDictIDName.Values); cmdKeyWords += " " + cmdKeyWords.ToUpper() + " " + cmdKeyWords.ToLower(); - secondaryKeyWords = String.Join(" ", RomInfo.ScriptComparisonOperatorsDict.Values) + " " + String.Join(" ", ScriptDatabase.specialOverworlds.Values) + " " + String.Join(" ", ScriptDatabase.overworldDirections.Values) + + " " + ScriptFile.ContainerTypes.Script.ToString() + + " " + ScriptFile.ContainerTypes.Function.ToString() + + " " + ScriptFile.ContainerTypes.Action.ToString() + " " + Event.EventType.Overworld + " " + Overworld.MovementCodeKW; secondaryKeyWords += " " + secondaryKeyWords.ToUpper() + " " + secondaryKeyWords.ToLower(); - // CREATE CONTROLS ScriptTextArea = new Scintilla(); scriptSearchManager = new SearchManager(EditorPanels.MainProgram, ScriptTextArea, panelFindScriptTextBox, PanelSearchScripts); scintillaScriptsPanel.Controls.Clear(); scintillaScriptsPanel.Controls.Add(ScriptTextArea); + FunctionTextArea = new Scintilla(); + functionSearchManager = new SearchManager(EditorPanels.MainProgram, FunctionTextArea, panelFindFunctionTextBox, PanelSearchFunctions); + scintillaFunctionsPanel.Controls.Clear(); + scintillaFunctionsPanel.Controls.Add(FunctionTextArea); + + ActionTextArea = new Scintilla(); + actionSearchManager = new SearchManager(EditorPanels.MainProgram, ActionTextArea, panelFindActionTextBox, PanelSearchActions); + scintillaActionsPanel.Controls.Clear(); + scintillaActionsPanel.Controls.Add(ActionTextArea); + + currentScintillaEditor = ScriptTextArea; + currentSearchManager = scriptSearchManager; + // BASIC CONFIG ScriptTextArea.TextChanged += (OnTextChangedScript); + FunctionTextArea.TextChanged += (OnTextChangedFunction); + ActionTextArea.TextChanged += (OnTextChangedAction); // INITIAL VIEW CONFIG InitialViewConfig(ScriptTextArea); + InitialViewConfig(FunctionTextArea); + InitialViewConfig(ActionTextArea); InitSyntaxColoring(ScriptTextArea); + InitSyntaxColoring(FunctionTextArea); + InitSyntaxColoring(ActionTextArea); // NUMBER MARGIN InitNumberMargin(ScriptTextArea, ScriptTextArea_MarginClick); + InitNumberMargin(FunctionTextArea, FunctionTextArea_MarginClick); + InitNumberMargin(ActionTextArea, ActionTextArea_MarginClick); // BOOKMARK MARGIN InitBookmarkMargin(ScriptTextArea); + InitBookmarkMargin(FunctionTextArea); + InitBookmarkMargin(ActionTextArea); // CODE FOLDING MARGIN InitCodeFolding(ScriptTextArea); + InitCodeFolding(FunctionTextArea); + InitCodeFolding(ActionTextArea); // INIT HOTKEYS InitHotkeys(ScriptTextArea, scriptSearchManager); + InitHotkeys(FunctionTextArea, functionSearchManager); + InitHotkeys(ActionTextArea, actionSearchManager); // INIT TOOLTIPS DWELLING /* ScriptTextArea.MouseDwellTime = 300; ScriptTextArea.DwellEnd += TextArea_DwellEnd; ScriptTextArea.DwellStart += TextArea_DwellStart; - FunctionTextArea.MouseDwellTime = 300; FunctionTextArea.DwellEnd += TextArea_DwellEnd; FunctionTextArea.DwellStart += TextArea_DwellStart; */ - - // Style for prefixed words (label_*, script_*) } - private void populate_selectScriptFileComboBox(int selectedIndex = 0) { + private void populate_selectScriptFileComboBox(int selectedIndex = 0) + { selectScriptFileComboBox.Items.Clear(); int scriptCount = Filesystem.GetScriptCount(); - for (int i = 0; i < scriptCount; i++) { + for (int i = 0; i < scriptCount; i++) + { // ScriptFile currentScriptFile = new ScriptFile(i, true, true); // selectScriptFileComboBox.Items.Add(currentScriptFile); selectScriptFileComboBox.Items.Add($"Script File {i}"); } - selectScriptFileComboBox.SelectedIndex = selectedIndex; } - - private void InitialViewConfig(Scintilla textArea) { + private void InitialViewConfig(Scintilla textArea) + { textArea.Dock = DockStyle.Fill; textArea.WrapMode = WrapMode.Word; textArea.IndentationGuides = IndentView.LookBoth; @@ -191,7 +209,9 @@ namespace DSPRE.Editors { textArea.WrapIndentMode = WrapIndentMode.Same; } - private void InitSyntaxColoring(Scintilla textArea) { + private void InitSyntaxColoring(Scintilla textArea) + { + // Configure the default style textArea.StyleResetDefault(); textArea.Styles[Style.Default].Font = "Consolas"; textArea.Styles[Style.Default].Size = 12; @@ -199,127 +219,67 @@ namespace DSPRE.Editors { textArea.Styles[Style.Default].ForeColor = Color.FromArgb(0xFFFFFF); textArea.StyleClearAll(); - // Configure Assembly lexer styles - textArea.Styles[Style.Asm.Identifier].ForeColor = Color.FromArgb(0xD0DAE2); - textArea.Styles[Style.Asm.Number].ForeColor = Color.FromArgb(0xFFFF00); - textArea.Styles[Style.Asm.String].ForeColor = Color.FromArgb(0xFF00FF); - textArea.Styles[Style.Asm.Character].ForeColor = Color.FromArgb(0xE95454); - textArea.Styles[Style.Asm.Operator].ForeColor = Color.FromArgb(0xFFFF00); - textArea.Styles[Style.Asm.Comment].ForeColor = Color.FromArgb(0x40BF57); + // Configure the lexer styles + textArea.Styles[Style.Python.Identifier].ForeColor = Color.FromArgb(0xD0DAE2); + textArea.Styles[Style.Python.CommentLine].ForeColor = Color.FromArgb(0x40BF57); + textArea.Styles[Style.Python.Number].ForeColor = Color.FromArgb(0xFFFF00); + textArea.Styles[Style.Python.String].ForeColor = Color.FromArgb(0xFF00FF); + textArea.Styles[Style.Python.Character].ForeColor = Color.FromArgb(0xE95454); + textArea.Styles[Style.Python.Operator].ForeColor = Color.FromArgb(0xFFFF00); + textArea.Styles[Style.Python.Word].ForeColor = Color.FromArgb(0x48A8EE); + textArea.Styles[Style.Python.Word2].ForeColor = Color.FromArgb(0xF98906); - // For command keywords - use CpuInstruction style - textArea.Styles[Style.Asm.CpuInstruction].ForeColor = Color.FromArgb(0x48A8EE); + textArea.Lexer = Lexer.Python; - // For secondary keywords - use Directive style - textArea.Styles[Style.Asm.Directive].ForeColor = Color.FromArgb(0xF98906); - - // Configure indicators for prefix highlighting - textArea.Indicators[0].Style = IndicatorStyle.TextFore; - textArea.Indicators[0].ForeColor = Color.FromArgb(0x8A2BE2); // Purple for label_* - textArea.Indicators[0].Under = false; // Draw over the lexer's styling - - textArea.Indicators[1].Style = IndicatorStyle.TextFore; - textArea.Indicators[1].ForeColor = Color.FromArgb(0x00CED1); // Cyan for script_* - textArea.Indicators[1].Under = false; // Draw over the lexer's styling - - // Set the lexer and keywords - textArea.Lexer = Lexer.Asm; - textArea.SetKeywords(0, cmdKeyWords); // CPU Instructions index - textArea.SetKeywords(3, secondaryKeyWords); // Directives index - - // Apply the highlighting - textArea.TextChanged += (sender, e) => HighlightPrefixedWords(textArea); - - // Initial highlighting - HighlightPrefixedWords(textArea); + textArea.SetKeywords(0, cmdKeyWords); + textArea.SetKeywords(1, secondaryKeyWords); } - private void HighlightPrefixedWords(Scintilla textArea) { - // Clear existing indicators - textArea.IndicatorCurrent = 0; - textArea.IndicatorClearRange(0, textArea.TextLength); - textArea.IndicatorCurrent = 1; - textArea.IndicatorClearRange(0, textArea.TextLength); - - // Process each line individually - for (int i = 0; i < textArea.Lines.Count; i++) { - string lineText = textArea.Lines[i].Text; - int linePos = textArea.Lines[i].Position; - - // Trim for detection but use original text for positions - string trimmedLine = lineText.Trim(); - - // Handle script_ lines - if (trimmedLine.Contains("script_") && trimmedLine.EndsWith(":")) { - int startPos = linePos + lineText.IndexOf("script_"); - int endPos = linePos + lineText.IndexOf(":", lineText.IndexOf("script_")) + 1; - - textArea.IndicatorCurrent = 1; - textArea.IndicatorFillRange(startPos, endPos - startPos); - } - - // Handle label_ lines - if (trimmedLine.Contains("label_") && trimmedLine.EndsWith(":")) { - int startPos = linePos + lineText.IndexOf("label_"); - int endPos = linePos + lineText.IndexOf(":", lineText.IndexOf("label_")) + 1; - - textArea.IndicatorCurrent = 0; - textArea.IndicatorFillRange(startPos, endPos - startPos); - } - } - } - - private void InitNumberMargin(Scintilla textArea, EventHandler textArea_MarginClick) { + private void InitNumberMargin(Scintilla textArea, EventHandler textArea_MarginClick) + { textArea.Styles[Style.LineNumber].BackColor = BACK_COLOR; textArea.Styles[Style.LineNumber].ForeColor = FORE_COLOR; textArea.Styles[Style.IndentGuide].ForeColor = FORE_COLOR; textArea.Styles[Style.IndentGuide].BackColor = BACK_COLOR; - Margin nums = textArea.Margins[NUMBER_MARGIN]; nums.Type = MarginType.Number; nums.Sensitive = true; nums.Mask = 0; - textArea.MarginClick += textArea_MarginClick; } - - private void InitBookmarkMargin(Scintilla textArea) { + private void InitBookmarkMargin(Scintilla textArea) + { //TextArea.SetFoldMarginColor(true, IntToColor(BACK_COLOR)); - Margin margin = textArea.Margins[BOOKMARK_MARGIN]; margin.Width = 20; margin.Sensitive = true; margin.Type = MarginType.Symbol; margin.Mask = (1 << BOOKMARK_MARKER); //margin.Cursor = MarginCursor.Arrow; - Marker marker = textArea.Markers[BOOKMARK_MARKER]; marker.Symbol = MarkerSymbol.Circle; marker.SetBackColor(Color.FromArgb(0xFF003B)); marker.SetForeColor(Color.FromArgb(0x000000)); marker.SetAlpha(100); } - - private void InitCodeFolding(Scintilla textArea) { + private void InitCodeFolding(Scintilla textArea) + { textArea.SetFoldMarginColor(true, BACK_COLOR); textArea.SetFoldMarginHighlightColor(true, BACK_COLOR); - // Enable code folding textArea.SetProperty("fold", "1"); textArea.SetProperty("fold.compact", "1"); - // Configure a margin to display folding symbols textArea.Margins[FOLDING_MARGIN].Type = MarginType.Symbol; textArea.Margins[FOLDING_MARGIN].Mask = Marker.MaskFolders; textArea.Margins[FOLDING_MARGIN].Sensitive = true; textArea.Margins[FOLDING_MARGIN].Width = 20; - // Set colors for all folding markers - for (int i = 25; i <= 31; i++) { + for (int i = 25; i <= 31; i++) + { textArea.Markers[i].SetForeColor(BACK_COLOR); // styles for [+] and [-] textArea.Markers[i].SetBackColor(FORE_COLOR); // styles for [+] and [-] } - // Configure folding markers with respective symbols textArea.Markers[Marker.Folder].Symbol = CODE_FOLDING_CIRCULAR ? MarkerSymbol.CirclePlus : MarkerSymbol.BoxPlus; textArea.Markers[Marker.FolderOpen].Symbol = CODE_FOLDING_CIRCULAR ? MarkerSymbol.CircleMinus : MarkerSymbol.BoxMinus; @@ -328,12 +288,11 @@ namespace DSPRE.Editors { textArea.Markers[Marker.FolderOpenMid].Symbol = CODE_FOLDING_CIRCULAR ? MarkerSymbol.CircleMinusConnected : MarkerSymbol.BoxMinusConnected; textArea.Markers[Marker.FolderSub].Symbol = MarkerSymbol.VLine; textArea.Markers[Marker.FolderTail].Symbol = MarkerSymbol.LCorner; - // Enable automatic folding textArea.AutomaticFold = (AutomaticFold.Show | AutomaticFold.Click | AutomaticFold.Change); } - - private void InitHotkeys(Scintilla scintillaTb, SearchManager sm) { + private void InitHotkeys(Scintilla scintillaTb, SearchManager sm) + { // register the hotkeys with the form HotKeyManager.AddHotKey(scintillaTb, sm.OpenSearch, Keys.F, true); HotKeyManager.AddHotKey(scintillaTb, () => Uppercase(scintillaTb), Keys.U, true); @@ -342,7 +301,6 @@ namespace DSPRE.Editors { HotKeyManager.AddHotKey(scintillaTb, () => ZoomOut(scintillaTb), Keys.OemMinus, true); HotKeyManager.AddHotKey(scintillaTb, () => ZoomDefault(scintillaTb), Keys.D0, true); HotKeyManager.AddHotKey(scintillaTb, sm.CloseSearch, Keys.Escape); - // remove conflicting hotkeys from scintilla scintillaTb.ClearCmdKey(Keys.Control | Keys.F); scintillaTb.ClearCmdKey(Keys.Control | Keys.R); @@ -350,86 +308,115 @@ namespace DSPRE.Editors { scintillaTb.ClearCmdKey(Keys.Control | Keys.L); scintillaTb.ClearCmdKey(Keys.Control | Keys.U); } - - private void Uppercase(Scintilla textArea) { + private void Uppercase(Scintilla textArea) + { // save the selection int start = textArea.SelectionStart; int end = textArea.SelectionEnd; - // modify the selected text textArea.ReplaceSelection(textArea.GetTextRange(start, end - start).ToUpper()); - // preserve the original selection textArea.SetSelection(start, end); } - - private void Lowercase(Scintilla textArea) { + private void Lowercase(Scintilla textArea) + { // save the selection int start = textArea.SelectionStart; int end = textArea.SelectionEnd; - // modify the selected text textArea.ReplaceSelection(textArea.GetTextRange(start, end - start).ToLower()); - // preserve the original selection textArea.SetSelection(start, end); } - - private void ZoomIn(Scintilla textArea) { + private void ZoomIn(Scintilla textArea) + { textArea.ZoomIn(); } - - private void ZoomOut(Scintilla textArea) { + private void ZoomOut(Scintilla textArea) + { textArea.ZoomOut(); } - - private void ZoomDefault(Scintilla textArea) { + private void ZoomDefault(Scintilla textArea) + { textArea.Zoom = 0; } - - private void ScriptEditorSetClean() { + private void ScriptEditorSetClean() + { Helpers.DisableHandlers(); - //scriptsTabPage.Text = ScriptFile.ContainerTypes.Script.ToString() + "s"; - scriptsDirty = false; + scriptsTabPage.Text = ScriptFile.ContainerTypes.Script.ToString() + "s"; + functionsTabPage.Text = ScriptFile.ContainerTypes.Function.ToString() + "s"; + actionsTabPage.Text = ScriptFile.ContainerTypes.Action.ToString() + "s"; + scriptsDirty = functionsDirty = actionsDirty = false; Helpers.EnableHandlers(); } - private void OnTextChangedScript(object sender, EventArgs e) { + private void OnTextChangedScript(object sender, EventArgs e) + { ScriptTextArea.Margins[NUMBER_MARGIN].Width = ScriptTextArea.Lines.Count.ToString().Length * 13; scriptsDirty = true; - //scriptsTabPage.Text = ScriptFile.ContainerTypes.Script.ToString() + "s" + "*"; + scriptsTabPage.Text = ScriptFile.ContainerTypes.Script.ToString() + "s" + "*"; } - private void ScriptTextArea_MarginClick(object sender, MarginClickEventArgs e) { + private void OnTextChangedFunction(object sender, EventArgs e) + { + FunctionTextArea.Margins[NUMBER_MARGIN].Width = FunctionTextArea.Lines.Count.ToString().Length * 13; + functionsDirty = true; + functionsTabPage.Text = ScriptFile.ContainerTypes.Function.ToString() + "s" + "*"; + } + + private void OnTextChangedAction(object sender, EventArgs e) + { + ActionTextArea.Margins[NUMBER_MARGIN].Width = ActionTextArea.Lines.Count.ToString().Length * 13; + actionsDirty = true; + actionsTabPage.Text = ScriptFile.ContainerTypes.Action.ToString() + "s" + "*"; + } + + private void ScriptTextArea_MarginClick(object sender, MarginClickEventArgs e) + { MarginClick(ScriptTextArea, e); } - private void MarginClick(Scintilla textArea, MarginClickEventArgs e) { - if (e.Margin == BOOKMARK_MARGIN) { + private void FunctionTextArea_MarginClick(object sender, MarginClickEventArgs e) + { + MarginClick(FunctionTextArea, e); + } + + private void ActionTextArea_MarginClick(object sender, MarginClickEventArgs e) + { + MarginClick(ActionTextArea, e); + } + + private void MarginClick(Scintilla textArea, MarginClickEventArgs e) + { + if (e.Margin == BOOKMARK_MARGIN) + { // Do we have a marker for this line? const uint mask = (1 << BOOKMARK_MARKER); Line line = textArea.Lines[textArea.LineFromPosition(e.Position)]; - if ((line.MarkerGet() & mask) > 0) { + if ((line.MarkerGet() & mask) > 0) + { // Remove existing bookmark line.MarkerDelete(BOOKMARK_MARKER); - } else { + } + else + { // Add bookmark line.MarkerAdd(BOOKMARK_MARKER); } } } - - private void selectScriptFileComboBox_SelectedIndexChanged(object sender, EventArgs e) { + private void selectScriptFileComboBox_SelectedIndexChanged(object sender, EventArgs e) + { DisplayScript(); } - - public void UpdateScriptNumberCheckBox(NumberStyles toSet) { + public void UpdateScriptNumberCheckBox(NumberStyles toSet) + { Helpers.DisableHandlers(); Properties.Settings.Default.scriptEditorFormatPreference = (int)toSet; - - switch ((NumberStyles)Properties.Settings.Default.scriptEditorFormatPreference) { + switch ((NumberStyles)Properties.Settings.Default.scriptEditorFormatPreference) + { case NumberStyles.None: scriptEditorNumberFormatNoPreference.Checked = true; break; @@ -440,341 +427,490 @@ namespace DSPRE.Editors { scriptEditorNumberFormatDecimal.Checked = true; break; } - Console.WriteLine("changed style to " + Properties.Settings.Default.scriptEditorFormatPreference); Helpers.EnableHandlers(); } - - private void UpdateScriptNumberFormat(NumberStyles numberStyle) { - if (Helpers.HandlersEnabled) { + private void UpdateScriptNumberFormat(NumberStyles numberStyle) + { + if (Helpers.HandlersEnabled) + { NumberStyles old = (NumberStyles)Properties.Settings.Default.scriptEditorFormatPreference; //Local Backup Properties.Settings.Default.scriptEditorFormatPreference = (int)numberStyle; - - if (!DisplayScript()) { + if (!DisplayScript()) + { UpdateScriptNumberCheckBox(old); //Restore old checkbox status! Script couldn't be redrawn } } } - - private void UpdateScriptNumberFormatNoPref(object sender, EventArgs e) { + private void UpdateScriptNumberFormatNoPref(object sender, EventArgs e) + { UpdateScriptNumberFormat(NumberStyles.None); } - - private void UpdateScriptNumberFormatDec(object sender, EventArgs e) { + private void UpdateScriptNumberFormatDec(object sender, EventArgs e) + { UpdateScriptNumberFormat(NumberStyles.Integer); } - - private void UpdateScriptNumberFormatHex(object sender, EventArgs e) { + private void UpdateScriptNumberFormatHex(object sender, EventArgs e) + { UpdateScriptNumberFormat(NumberStyles.HexNumber); } - - private bool DisplayScript() { + private bool DisplayScript() + { Console.WriteLine("Script Reload has been requested"); - /* clear controls */ - if (Helpers.HandlersDisabled || selectScriptFileComboBox.SelectedItem == null) { + if (Helpers.HandlersDisabled || selectScriptFileComboBox.SelectedItem == null) + { return false; } - // Keep all the code that handles unsaved changes - if (scriptsDirty || functionsDirty || actionsDirty) { + if (scriptsDirty || functionsDirty || actionsDirty) + { DialogResult d = MessageBox.Show("There are unsaved changes in this Script File.\nDo you wish to discard them?", "Unsaved work", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); - if (!d.Equals(DialogResult.Yes)) { + if (!d.Equals(DialogResult.Yes)) + { Helpers.DisableHandlers(); + // selectScriptFileComboBox.SelectedItem = currentScriptFile; selectScriptFileComboBox.SelectedIndex = (int)currentScriptFile.fileID; Helpers.EnableHandlers(); return false; } } - Helpers.DisableHandlers(); ScriptFile lastScriptFile = currentScriptFile; - // Load the script file using our new label-based constructor - currentScriptFile = new ScriptFile(selectScriptFileComboBox.SelectedIndex); + // currentScriptFile = (ScriptFile)selectScriptFileComboBox.SelectedItem; + currentScriptFile = new ScriptFile(selectScriptFileComboBox.SelectedIndex); // Load script file - // Clear only the script text area and nav listbox ScriptTextArea.ClearAll(); - scriptsNavListbox.Items.Clear(); + FunctionTextArea.ClearAll(); + ActionTextArea.ClearAll(); + scriptsNavListbox.Items.Clear(); + functionsNavListbox.Items.Clear(); + actionsNavListbox.Items.Clear(); + + //prevent buttons from flickering when the combobox selection changes bool typeChanged = true; - if (lastScriptFile != null) { + if (lastScriptFile != null) + { typeChanged = lastScriptFile.isLevelScript != currentScriptFile.isLevelScript; } - - if (typeChanged) { - if (currentScriptFile.isLevelScript) { + if (typeChanged) + { + if (currentScriptFile.isLevelScript) + { importScriptFileButton.Location = initial_importScriptFileButton_location; exportScriptFileButton.Location = initial_exportScriptFileButton_location; addScriptFileButton.Location = initial_addScriptFileButton_location; removeScriptFileButton.Location = initial_removeScriptFileButton_location; - viewLevelScriptButton.Location = new_viewLevelScript_location; - importScriptFileButton.Enabled = false; exportScriptFileButton.Enabled = false; addScriptFileButton.Enabled = false; removeScriptFileButton.Enabled = false; - viewLevelScriptButton.Enabled = true; - } else { + } + else + { importScriptFileButton.Location = new_importScriptFileButton_location; exportScriptFileButton.Location = new_exportScriptFileButton_location; addScriptFileButton.Location = new_addScriptFileButton_location; removeScriptFileButton.Location = new_removeScriptFileButton_location; - viewLevelScriptButton.Location = initial_viewLevelScript_location; - importScriptFileButton.Enabled = true; exportScriptFileButton.Enabled = true; addScriptFileButton.Enabled = true; removeScriptFileButton.Enabled = true; - viewLevelScriptButton.Enabled = false; } } - if (!currentScriptFile.isLevelScript) { - displayScriptFile(scriptsNavListbox, ScriptTextArea); + if (!currentScriptFile.isLevelScript) + { + displayScriptFile(ScriptFile.ContainerTypes.Script, currentScriptFile.allScripts, scriptsNavListbox, ScriptTextArea); + displayScriptFile(ScriptFile.ContainerTypes.Function, currentScriptFile.allFunctions, functionsNavListbox, FunctionTextArea); + displayScriptFileActions(ScriptFile.ContainerTypes.Action, currentScriptFile.allActions, actionsNavListbox, ActionTextArea); + } + + ScriptEditorSetClean(); + Helpers.statusLabelMessage(); + Helpers.EnableHandlers(); + return true; } - ScriptEditorSetClean(); + static void displayScriptFile(ScriptFile.ContainerTypes containerType, List commandList, ListBox navListBox, Scintilla textArea) + { + string buffer = ""; + /* Add commands */ + for (int i = 0; i < commandList.Count; i++) + { + ScriptCommandContainer scriptCommandContainer = commandList[i]; - Helpers.statusLabelMessage(); - Helpers.EnableHandlers(); + /* Write header */ + string header = containerType + " " + (i + 1); + buffer += header + ':' + Environment.NewLine; + navListBox.Items.Add(header); - return true; - } + /* If current command is identical to another, print UseScript instead of commands */ + if (scriptCommandContainer.usedScriptID < 0) + { + for (int j = 0; j < scriptCommandContainer.commands.Count; j++) + { + ScriptCommand command = scriptCommandContainer.commands[j]; + if (!ScriptDatabase.endCodes.Contains(command.id)) + { + buffer += '\t'; + } - private void displayScriptFile(ListBox navListBox, Scintilla textArea) { - if (currentScriptFile.CommandSequence == null || currentScriptFile.CommandSequence.Count == 0) { - return; - } + buffer += command.name + Environment.NewLine; + } + } + else + { + buffer += '\t' + "UseScript_#" + scriptCommandContainer.usedScriptID + Environment.NewLine; + } - // First add all labels to the nav listbox - HashSet addedLabels = new HashSet(); - foreach (var cmdPos in currentScriptFile.CommandSequence) { - if (!string.IsNullOrEmpty(cmdPos.Label) && !addedLabels.Contains(cmdPos.Label)) { - navListBox.Items.Add(cmdPos.Label); - addedLabels.Add(cmdPos.Label); + textArea.AppendText(buffer + Environment.NewLine); + buffer = ""; } } - // Generate the script text - string scriptText = currentScriptFile.ToText(); - textArea.Text = scriptText; + static void displayScriptFileActions(ScriptFile.ContainerTypes containerType, List commandList, ListBox navListBox, Scintilla textArea) + { + /* Add movements */ + string buffer = ""; + for (int i = 0; i < commandList.Count; i++) + { + ScriptActionContainer currentCommand = commandList[i]; + + string header = containerType + " " + (i + 1); + buffer += header + ':' + Environment.NewLine; + navListBox.Items.Add(header); + + for (int j = 0; j < currentCommand.commands.Count; j++) + { + ScriptAction command = currentCommand.commands[j]; + if (!ScriptDatabase.movementEndCodes.Contains(command.id)) + { + buffer += '\t'; + } + + buffer += command.name + Environment.NewLine; + } + + textArea.AppendText(buffer + Environment.NewLine); + buffer = ""; + } + } + + private void scriptEditorZoomInButton_Click(object sender, EventArgs e) + { + ZoomIn(currentScintillaEditor); } - private void scriptEditorZoomInButton_Click(object sender, EventArgs e) { - ZoomIn(ScriptTextArea); + private void scriptEditorZoomOutButton_Click(object sender, EventArgs e) + { + ZoomOut(currentScintillaEditor); } - private void scriptEditorZoomOutButton_Click(object sender, EventArgs e) { - ZoomOut(ScriptTextArea); + private void scriptEditorZoomResetButton_Click(object sender, EventArgs e) + { + ZoomDefault(currentScintillaEditor); } - private void scriptEditorZoomResetButton_Click(object sender, EventArgs e) { - ZoomDefault(ScriptTextArea); + private void scriptEditorTabControl_TabIndexChanged(object sender, EventArgs e) + { + if (scriptEditorTabControl.SelectedTab == scriptsTabPage) + { + currentSearchManager = scriptSearchManager; + currentScintillaEditor = ScriptTextArea; + } + else if (scriptEditorTabControl.SelectedTab == functionsTabPage) + { + currentSearchManager = functionSearchManager; + currentScintillaEditor = FunctionTextArea; + } + else + { + //Actions + currentSearchManager = actionSearchManager; + currentScintillaEditor = ActionTextArea; + } } - private void removeScriptFileButton_Click(object sender, EventArgs e) { + private void removeScriptFileButton_Click(object sender, EventArgs e) + { DialogResult d = MessageBox.Show("Are you sure you want to delete the last Script File?", "Confirm deletion", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); - if (d.Equals(DialogResult.Yes)) { + if (d.Equals(DialogResult.Yes)) + { /* Delete script file */ string path = Filesystem.GetScriptPath(selectScriptFileComboBox.Items.Count - 1); File.Delete(path); - /* Check if currently selected file is the last one, and in that case select the one before it */ int lastIndex = selectScriptFileComboBox.Items.Count - 1; - if (selectScriptFileComboBox.SelectedIndex == lastIndex) { + if (selectScriptFileComboBox.SelectedIndex == lastIndex) + { selectScriptFileComboBox.SelectedIndex--; } - /* Remove item from ComboBox */ selectScriptFileComboBox.Items.RemoveAt(lastIndex); } } - - private void addScriptFileButton_Click(object sender, EventArgs e) { + private void addScriptFileButton_Click(object sender, EventArgs e) + { /* Add new event file to event folder */ int fileID = selectScriptFileComboBox.Items.Count; - // Create a simple script with one labeled section - List scriptLines = new List { - "script_0:", - "\tEnd" - }; + ScriptFile scriptFile = new ScriptFile( + scriptLines: new Scintilla { Text = "Script 1:\nEnd" }.Lines.ToStringsList(trim: true), + functionLines: null, + actionLines: null, + fileID + ); - // Use the new constructor that just takes script lines - ScriptFile scriptFile = new ScriptFile(scriptLines, fileID); - - // Check if ScriptFile instance was created successfully - if (scriptFile.SaveToFileDefaultDir(fileID, showSuccessMessage: false)) { + //check if ScriptFile instance was created successfully + if (scriptFile.SaveToFileDefaultDir(fileID, showSuccessMessage: false)) + { /* Update ComboBox and select new file */ - selectScriptFileComboBox.Items.Add($"Script File {fileID}"); - selectScriptFileComboBox.SelectedIndex = selectScriptFileComboBox.Items.Count - 1; + selectScriptFileComboBox.Items.Add(scriptFile); + selectScriptFileComboBox.SelectedItem = scriptFile; } } - private void saveScriptFileButton_Click(object sender, EventArgs e) { + private void saveScriptFileButton_Click(object sender, EventArgs e) + { /* Create new ScriptFile object using the values in the script editor */ int fileID = currentScriptFile.fileID; - // We only need the script text area now, not function or action areas ScriptFile userEdited = new ScriptFile( - ScriptTextArea.Lines.ToStringsList(trim: true), - fileID + scriptLines: ScriptTextArea.Lines.ToStringsList(trim: true), + functionLines: FunctionTextArea.Lines.ToStringsList(trim: true), + actionLines: ActionTextArea.Lines.ToStringsList(trim: true), + fileID ); - // Check if ScriptFile instance was created successfully - if (userEdited.SaveToFileDefaultDir(fileID)) { + if (userEdited.hasNoScripts) + { + MessageBox.Show("This " + nameof(ScriptFile) + " couldn't be saved. A minimum of one script is required.", "Can't save", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + //check if ScriptFile instance was created successfully + if (userEdited.SaveToFileDefaultDir(fileID)) + { currentScriptFile = userEdited; ScriptEditorSetClean(); } } - - private void exportScriptFileButton_Click(object sender, EventArgs e) { + private void exportScriptFileButton_Click(object sender, EventArgs e) + { currentScriptFile.SaveToFileExplorePath(currentScriptFile.ToString(), blindmode: true); } - - private void importScriptFileButton_Click(object sender, EventArgs e) { + private void importScriptFileButton_Click(object sender, EventArgs e) + { /* Prompt user to select .scr or .bin file */ - OpenFileDialog of = new OpenFileDialog { + OpenFileDialog of = new OpenFileDialog + { Filter = "Script File (*.scr, *.bin)|*.scr;*.bin" }; - if (of.ShowDialog(this) != DialogResult.OK) { + if (of.ShowDialog(this) != DialogResult.OK) + { return; } - /* Update scriptFile object in memory */ int i = selectScriptFileComboBox.SelectedIndex; string path = Filesystem.GetScriptPath(i); File.Copy(of.FileName, path, true); - populate_selectScriptFileComboBox(i); - /* Refresh controls */ selectScriptFileComboBox_SelectedIndexChanged(null, null); - /* Display success message */ MessageBox.Show("Scripts imported successfully!", "", MessageBoxButtons.OK, MessageBoxIcon.Information); } - - private void viewLevelScriptButton_Click(object sender, EventArgs e) { + private void viewLevelScriptButton_Click(object sender, EventArgs e) + { EditorPanels.levelScriptEditor.OpenLevelScriptEditor(this._parent, selectScriptFileComboBox.SelectedIndex); } - - private void locateCurrentScriptFile_Click(object sender, EventArgs e) { + private void locateCurrentScriptFile_Click(object sender, EventArgs e) + { string path = Filesystem.GetScriptPath(selectScriptFileComboBox.SelectedIndex); Helpers.ExplorerSelect(path); } - - private void findNext(SearchManager searchManager) { + private void findNext(SearchManager searchManager) + { searchManager.Find(true, false); scrollResultToTop(searchManager); } - - private void findPrev(SearchManager searchManager) { + private void findPrev(SearchManager searchManager) + { searchManager.Find(false, false); scrollResultToTop(searchManager); } - - private void findCurrent(SearchManager searchManager) { + private void findCurrent(SearchManager searchManager) + { searchManager.Find(true, true); scrollResultToTop(searchManager); } - - private void TxtFindKeyDown(SearchManager searchManager, KeyEventArgs e) { - if (HotKeyManager.IsHotkey(e, Keys.Enter)) { + private void TxtFindKeyDown(SearchManager searchManager, KeyEventArgs e) + { + if (HotKeyManager.IsHotkey(e, Keys.Enter)) + { findNext(searchManager); } - - if (HotKeyManager.IsHotkey(e, Keys.Enter, true) || HotKeyManager.IsHotkey(e, Keys.Enter, false, true)) { + if (HotKeyManager.IsHotkey(e, Keys.Enter, true) || HotKeyManager.IsHotkey(e, Keys.Enter, false, true)) + { findPrev(searchManager); } } - - private void BtnNextFindScript_Click(object sender, EventArgs e) { + private void BtnNextFindScript_Click(object sender, EventArgs e) + { findNext(scriptSearchManager); } - - private void BtnPrevFindScript_Click(object sender, EventArgs e) { + private void BtnPrevFindScript_Click(object sender, EventArgs e) + { findPrev(scriptSearchManager); } - - private void panelFindScriptTextBox_TextChanged(object sender, EventArgs e) { + private void panelFindScriptTextBox_TextChanged(object sender, EventArgs e) + { findCurrent(scriptSearchManager); } - - private void scriptTxtFind_KeyDown(object sender, KeyEventArgs e) { + private void scriptTxtFind_KeyDown(object sender, KeyEventArgs e) + { TxtFindKeyDown(scriptSearchManager, e); } - - private void BtnCloseFindScript_Click(object sender, EventArgs e) { + private void BtnCloseFindScript_Click(object sender, EventArgs e) + { scriptSearchManager.CloseSearch(); } + private void BtnNextFindFunc_Click(object sender, EventArgs e) + { + findNext(functionSearchManager); + } - void scrollResultToTop(SearchManager searchManager) { + private void BtnPrevFindFunc_Click(object sender, EventArgs e) + { + findNext(functionSearchManager); + } + + private void panelFindFunctionTextBox_TextChanged(object sender, EventArgs e) + { + findNext(functionSearchManager); + } + + private void functionTxtFind_KeyDown(object sender, KeyEventArgs e) + { + TxtFindKeyDown(functionSearchManager, e); + } + + private void BtnCloseFindFunc_Click(object sender, EventArgs e) + { + functionSearchManager.CloseSearch(); + } + + private void BtnNextFindActions_Click(object sender, EventArgs e) + { + findNext(actionSearchManager); + } + + private void BtnPrevFindActions_Click(object sender, EventArgs e) + { + findNext(actionSearchManager); + } + + private void panelFindActionTextBox_TextChanged(object sender, EventArgs e) + { + findNext(actionSearchManager); + } + + private void actionTxtFind_KeyDown(object sender, KeyEventArgs e) + { + TxtFindKeyDown(actionSearchManager, e); + } + + private void BtnCloseFindActions_Click(object sender, EventArgs e) + { + actionSearchManager.CloseSearch(); + } + + void scrollResultToTop(SearchManager searchManager) + { int resultStart = searchManager.textAreaScintilla.CurrentLine - ScriptEditorSearchResult.ResultsPadding; searchManager.textAreaScintilla.FirstVisibleLine = resultStart; } - private void NavigatorGoTo(ListBox listBox, ScriptFile.ContainerTypes containerType) { - if (listBox.SelectedIndex < 0) { + private void NavigatorGoTo(ListBox listBox, TabPage tabPage, SearchManager searchManager, ScriptFile.ContainerTypes containerType) + { + if (listBox.SelectedIndex < 0) + { return; } + scriptEditorTabControl.SelectedTab = tabPage; int commandNumber = listBox.SelectedIndex + 1; string CommandBlockOpen = $"{containerType} {commandNumber}:"; - scriptSearchManager.Find(true, false, CommandBlockOpen); + searchManager.Find(true, false, CommandBlockOpen); - scrollResultToTop(scriptSearchManager); + scrollResultToTop(searchManager); } - private void scriptsNavListbox_SelectedIndexChanged(object sender, EventArgs e) { - NavigatorGoTo((ListBox)sender, ScriptFile.ContainerTypes.Script); + private void scriptsNavListbox_SelectedIndexChanged(object sender, EventArgs e) + { + NavigatorGoTo((ListBox)sender, scriptsTabPage, scriptSearchManager, ScriptFile.ContainerTypes.Script); } - private void functionsNavListbox_SelectedIndexChanged(object sender, EventArgs e) { - //NavigatorGoTo((ListBox)sender, functionsTabPage, functionSearchManager, ScriptFile.ContainerTypes.Function); + private void functionsNavListbox_SelectedIndexChanged(object sender, EventArgs e) + { + NavigatorGoTo((ListBox)sender, functionsTabPage, functionSearchManager, ScriptFile.ContainerTypes.Function); } - private void actionsNavListbox_SelectedIndexChanged(object sender, EventArgs e) { - //NavigatorGoTo((ListBox)sender, actionsTabPage, actionSearchManager, ScriptFile.ContainerTypes.Action); + private void actionsNavListbox_SelectedIndexChanged(object sender, EventArgs e) + { + NavigatorGoTo((ListBox)sender, actionsTabPage, actionSearchManager, ScriptFile.ContainerTypes.Action); } - private void openFindScriptEditorButton_Click(object sender, EventArgs e) { - scriptSearchManager.OpenSearch(); + private void openFindScriptEditorButton_Click(object sender, EventArgs e) + { + currentSearchManager.OpenSearch(); } - private void ScriptEditorExpandButton_Click(object sender, EventArgs e) { - ScriptTextArea.FoldAll(FoldAction.Expand); + private void ScriptEditorExpandButton_Click(object sender, EventArgs e) + { + currentScintillaEditor.FoldAll(FoldAction.Expand); } - private void ScriptEditorCollapseButton_Click(object sender, EventArgs e) { - ScriptTextArea.FoldAll(FoldAction.Contract); + private void ScriptEditorCollapseButton_Click(object sender, EventArgs e) + { + currentScintillaEditor.FoldAll(FoldAction.Contract); } - private void scriptEditorWordWrapCheckbox_CheckedChanged(object sender, EventArgs e) { + private void scriptEditorWordWrapCheckbox_CheckedChanged(object sender, EventArgs e) + { ScriptTextArea.WrapMode = scriptEditorWordWrapCheckbox.Checked ? WrapMode.Word : WrapMode.None; + FunctionTextArea.WrapMode = scriptEditorWordWrapCheckbox.Checked ? WrapMode.Word : WrapMode.None; + ActionTextArea.WrapMode = scriptEditorWordWrapCheckbox.Checked ? WrapMode.Word : WrapMode.None; } - private void viewWhiteSpacesButton_Click(object sender, EventArgs e) { + private void viewWhiteSpacesButton_Click(object sender, EventArgs e) + { ScriptTextArea.ViewWhitespace = scriptEditorWhitespacesCheckbox.Checked ? WhitespaceMode.VisibleAlways : WhitespaceMode.Invisible; + FunctionTextArea.ViewWhitespace = scriptEditorWhitespacesCheckbox.Checked ? WhitespaceMode.VisibleAlways : WhitespaceMode.Invisible; + ActionTextArea.ViewWhitespace = scriptEditorWhitespacesCheckbox.Checked ? WhitespaceMode.VisibleAlways : WhitespaceMode.Invisible; } - private void searchInScriptsTextBox_KeyDown(object sender, KeyEventArgs e) { - if (e.KeyCode == Keys.Enter) { + private void searchInScriptsTextBox_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Enter) + { searchInScriptsButton_Click(null, null); } } - - public List getScriptsToSearch() { + public List getScriptsToSearch() + { List scriptsToSearch = new List(); - - if (searchOnlyCurrentScriptCheckBox.Checked) { + if (searchOnlyCurrentScriptCheckBox.Checked) + { this.UIThread(() => { searchProgressBar.Maximum = 1; }); @@ -785,11 +921,14 @@ namespace DSPRE.Editors { this.UIThread(() => { searchProgressBar.IncrementNoAnimation(); }); - } else { + } + else + { this.UIThread(() => { searchProgressBar.Maximum = selectScriptFileComboBox.Items.Count; }); - for (int i = 0; i < selectScriptFileComboBox.Items.Count; i++) { + for (int i = 0; i < selectScriptFileComboBox.Items.Count; i++) + { ScriptFile scriptFile = new ScriptFile(i); Console.WriteLine("Attempting to load script " + scriptFile.fileID); scriptsToSearch.Add(scriptFile); @@ -798,42 +937,37 @@ namespace DSPRE.Editors { }); } } - return scriptsToSearch; } - - private void searchInScriptsButton_Click(object sender, EventArgs e) { - if (searchInScriptsTextBox.Text == "") { + private void searchInScriptsButton_Click(object sender, EventArgs e) + { + if (searchInScriptsTextBox.Text == "") + { return; } - BackgroundWorker bw = new BackgroundWorker(); bw.DoWork += (_sender, args) => { - this.UIThread(() => { - searchInScriptsResultListBox.Items.Clear(); - searchProgressBar.Value = 0; - }); + this.UIThread(() => { + searchInScriptsResultListBox.Items.Clear(); + searchProgressBar.Value = 0; + }); + List scriptsToSearch = getScriptsToSearch(); - List scriptsToSearch = getScriptsToSearch(); - - string searchString = searchInScriptsTextBox.Text; - bool searchCriteriaCS(string s) => s.IndexOf(searchString, StringComparison.InvariantCulture) >= 0; - bool searchCriteriaCI(string s) => s.IndexOf(searchString, StringComparison.InvariantCultureIgnoreCase) >= 0; - - Func searchCriteria; - if (scriptSearchCaseSensitiveCheckBox.Checked) { - searchCriteria = searchCriteriaCS; - } else { - searchCriteria = searchCriteriaCI; - } - - List results = new List(); - foreach (ScriptFile scriptFile in scriptsToSearch) { - List scriptResults = SearchInScripts(scriptFile, searchCriteria); - results.AddRange(scriptResults); - // results.AddRange(actionResults); - } + string searchString = searchInScriptsTextBox.Text; + Func searchCriteriaCS = (string s) => s.IndexOf(searchString, StringComparison.InvariantCulture) >= 0; + Func searchCriteriaCI = (string s) => s.IndexOf(searchString, StringComparison.InvariantCultureIgnoreCase) >= 0; + Func searchCriteria = scriptSearchCaseSensitiveCheckBox.Checked ? searchCriteriaCS : searchCriteriaCI; + List results = new List(); + foreach (ScriptFile scriptFile in scriptsToSearch) + { + List scriptResults = SearchInScripts(scriptFile, scriptFile.allScripts, searchCriteria); + List functionResults = SearchInScripts(scriptFile, scriptFile.allFunctions, searchCriteria); + // List actionResults = SearchInScripts(scriptFile, scriptFile.allActions, searchCriteria); + results.AddRange(scriptResults); + results.AddRange(functionResults); + // results.AddRange(actionResults); + } this.UIThread(() => { searchInScriptsResultListBox.Items.AddRange(results.ToArray()); searchProgressBar.Value = 0; @@ -843,50 +977,84 @@ namespace DSPRE.Editors { bw.RunWorkerAsync(); } - private List SearchInScripts(ScriptFile scriptFile, Func criteria) { + private List SearchInScripts(ScriptFile scriptFile, List commandContainers, Func criteria) + { List results = new List(); + for (int j = 0; j < commandContainers.Count; j++) + { + if (commandContainers[j].commands is null) + { + continue; + } + + ScriptCommandContainer scriptCommandContainer = commandContainers[j]; + foreach (ScriptCommand scriptCommand in scriptCommandContainer.commands) + { + if (criteria(scriptCommand.name)) + { + results.Add(new ScriptEditorSearchResult(scriptFile, scriptCommandContainer.containerType, j + 1, scriptCommand)); + } + } + } return results; } - - private void searchInScriptsResultListBox_KeyDown(object sender, KeyEventArgs e) { - if (e.KeyCode == Keys.Enter) { + private void searchInScriptsResultListBox_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Enter) + { goToSearchResult(); } } - - private void searchInScripts_GoToEntryResult(object sender, MouseEventArgs e) { + private void searchInScripts_GoToEntryResult(object sender, MouseEventArgs e) + { goToSearchResult(); } - - private void goToSearchResult() { + private void goToSearchResult() + { if (searchInScriptsResultListBox.SelectedItem == null) { return; } - ScriptEditorSearchResult searchResult = (ScriptEditorSearchResult)searchInScriptsResultListBox.SelectedItem; ScriptFile scriptFile = searchResult.scriptFile; ScriptFile.ContainerTypes containerType = searchResult.containerType; - selectScriptFileComboBox.SelectedIndex = scriptFile.fileID; - if (containerType == ScriptFile.ContainerTypes.Script) { - displaySearchResult(scriptSearchManager, searchResult); + if (containerType == ScriptFile.ContainerTypes.Script) + { + displaySearchResult(scriptsTabPage, scriptSearchManager, searchResult); + } + else if (containerType == ScriptFile.ContainerTypes.Function) + { + displaySearchResult(functionsTabPage, functionSearchManager, searchResult); + } + else if (containerType == ScriptFile.ContainerTypes.Action) + { + displaySearchResult(actionsTabPage, actionSearchManager, searchResult); } } - private void displaySearchResult(SearchManager searchManager, ScriptEditorSearchResult searchResult) { + private void displaySearchResult(TabPage tabPage, SearchManager searchManager, ScriptEditorSearchResult searchResult) + { + if (scriptEditorTabControl.SelectedTab != tabPage) + { + scriptEditorTabControl.SelectedTab = tabPage; + } + searchManager.Find(true, false, searchResult.CommandBlockOpen); int blockStart = searchManager.textAreaScintilla.CurrentLine - ScriptEditorSearchResult.ResultsPadding; searchManager.Find(true, false, searchResult.scriptCommand.name); int resultStart = searchManager.textAreaScintilla.CurrentLine - ScriptEditorSearchResult.ResultsPadding; - - if (scrollToBlockStartcheckBox.Checked) { + if (scrollToBlockStartcheckBox.Checked) + { searchManager.textAreaScintilla.FirstVisibleLine = blockStart; - } else { + } + else + { searchManager.textAreaScintilla.FirstVisibleLine = resultStart; } } + } public class ScriptEditorSearchResult { diff --git a/DS_Map/Main Window.cs b/DS_Map/Main Window.cs index 2c9fba5..636fa05 100644 --- a/DS_Map/Main Window.cs +++ b/DS_Map/Main Window.cs @@ -5494,12 +5494,14 @@ namespace DSPRE { } else { ScriptFile itemScript = new ScriptFile(RomInfo.itemScriptFileNumber); owItemComboBox.Items.Clear(); - //foreach (ScriptCommandContainer cont in itemScript.allScripts) { - // if (cont.commands.Count > 4) { - // continue; - // } - // owItemComboBox.Items.Add(BitConverter.ToUInt16(cont.commands[1].Parameters[1].RawData, 0) + "x " + itemNames[BitConverter.ToUInt16(cont.commands[0].Parameters[1].RawData, 0)]); - //} + foreach (ScriptCommandContainer cont in itemScript.allScripts) + { + if (cont.commands.Count > 4) + { + continue; + } + owItemComboBox.Items.Add(BitConverter.ToUInt16(cont.commands[1].cmdParams[1], 0) + "x " + itemNames[BitConverter.ToUInt16(cont.commands[0].cmdParams[1], 0)]); + } } /* Add ow movement list to box */ diff --git a/DS_Map/PatchToolboxDialog.cs b/DS_Map/PatchToolboxDialog.cs index c96b456..bbbae98 100644 --- a/DS_Map/PatchToolboxDialog.cs +++ b/DS_Map/PatchToolboxDialog.cs @@ -258,18 +258,18 @@ namespace DSPRE public static bool CheckScriptsStandardizedItemNumbers() { ScriptFile itemScript = new ScriptFile(RomInfo.itemScriptFileNumber); - //if (itemScript.allScripts.Count - 1 < new TextArchive(RomInfo.itemNamesTextNumber).messages.Count) - //{ - // return false; - //} + if (itemScript.allScripts.Count - 1 < new TextArchive(RomInfo.itemNamesTextNumber).messages.Count) + { + return false; + } - //for (ushort i = 0; i < itemScript.allScripts.Count - 1; i++) - //{ - // if (BitConverter.ToUInt16(itemScript.allScripts[i].commands[0].Parameters[1].RawData, 0) != i || BitConverter.ToUInt16(itemScript.allScripts[i].commands[1].Parameters[1].RawData, 0) != 1) - // { - // return false; - // } - //} + for (ushort i = 0; i < itemScript.allScripts.Count - 1; i++) + { + if (BitConverter.ToUInt16(itemScript.allScripts[i].commands[0].cmdParams[1], 0) != i || BitConverter.ToUInt16(itemScript.allScripts[i].commands[1].cmdParams[1], 0) != 1) + { + return false; + } + } return true; } @@ -590,12 +590,13 @@ namespace DSPRE ScriptFile itemScriptFile = new ScriptFile(RomInfo.itemScriptFileNumber); // Create map for: script no. -> vanilla item - //int[] vanillaItemsArray = new int[itemScriptFile.allScripts.Count - 1]; - // - //for (int i = 0; i < itemScriptFile.allScripts.Count - 1; i++) - //{ - // vanillaItemsArray[i] = BitConverter.ToInt16(itemScriptFile.allScripts[i].commands[0].Parameters[1].RawData, 0); - //}; + int[] vanillaItemsArray = new int[itemScriptFile.allScripts.Count - 1]; + + for (int i = 0; i < itemScriptFile.allScripts.Count - 1; i++) + { + vanillaItemsArray[i] = BitConverter.ToInt16(itemScriptFile.allScripts[i].commands[0].cmdParams[1], 0); + } + ; // Parse all event files and fix instances of ground items according to the new order int cnt = Filesystem.GetEventFileCount(); @@ -617,7 +618,7 @@ namespace DSPRE if (isItem) { int itemScriptID = eventFile.overworlds[j].scriptNumber - (itemScrMin - 1); - //eventFile.overworlds[j].scriptNumber = (ushort)(itemScrMin + vanillaItemsArray[itemScriptID - 1]); + eventFile.overworlds[j].scriptNumber = (ushort)(itemScrMin + vanillaItemsArray[itemScriptID - 1]); dirty = true; } } @@ -644,29 +645,29 @@ namespace DSPRE using (DSUtils.EasyWriter ewr = new DSUtils.EasyWriter(ow9path, ow9offs)) { - //ewr.Write((ushort)(itemScrMin + vanillaItemsArray[itemScriptID - 1])); + ewr.Write((ushort)(itemScrMin + vanillaItemsArray[itemScriptID - 1])); } } // Sort scripts in the Script File according to item indices int itemCount = new TextArchive(RomInfo.itemNamesTextNumber).messages.Count; - //ScriptCommandContainer executeGive = new ScriptCommandContainer((uint)itemCount + 1, itemScriptFile.allScripts[itemScriptFile.allScripts.Count - 1]); + ScriptCommandContainer executeGive = new ScriptCommandContainer((uint)itemCount + 1, itemScriptFile.allScripts[itemScriptFile.allScripts.Count - 1]); - //itemScriptFile.allScripts.Clear(); + itemScriptFile.allScripts.Clear(); - //for (ushort i = 0; i < itemCount; i++) - //{ - // List cmdList = new List { - // new ScriptCommand("SetVar 0x8008 " + i), - // new ScriptCommand("SetVar 0x8009 0x1"), - // new ScriptCommand("Jump Function_#1") - // }; - // - // itemScriptFile.allScripts.Add(new ScriptCommandContainer((ushort)(i + 1), ScriptFile.ContainerTypes.Script, commandList: cmdList)); - //} - // - //itemScriptFile.allScripts.Add(executeGive); - //itemScriptFile.allFunctions[0].usedScriptID = itemCount + 1; + for (ushort i = 0; i < itemCount; i++) + { + List cmdList = new List { + new ScriptCommand("SetVar 0x8008 " + i), + new ScriptCommand("SetVar 0x8009 0x1"), + new ScriptCommand("Jump Function_#1") + }; + + itemScriptFile.allScripts.Add(new ScriptCommandContainer((ushort)(i + 1), ScriptFile.ContainerTypes.Script, commandList: cmdList)); + } + + itemScriptFile.allScripts.Add(executeGive); + itemScriptFile.allFunctions[0].usedScriptID = itemCount + 1; itemScriptFile.SaveToFileDefaultDir(RomInfo.itemScriptFileNumber, showSuccessMessage: false); MessageBox.Show("Operation successful.", "Process completed.", MessageBoxButtons.OK, MessageBoxIcon.Information); diff --git a/DS_Map/Script/LevelScriptTrigger.cs b/DS_Map/ROMFiles/LevelScriptTrigger.cs similarity index 100% rename from DS_Map/Script/LevelScriptTrigger.cs rename to DS_Map/ROMFiles/LevelScriptTrigger.cs diff --git a/DS_Map/Script/MapScreenLoadTrigger.cs b/DS_Map/ROMFiles/MapScreenLoadTrigger.cs similarity index 100% rename from DS_Map/Script/MapScreenLoadTrigger.cs rename to DS_Map/ROMFiles/MapScreenLoadTrigger.cs diff --git a/DS_Map/Script/ScriptAction.cs b/DS_Map/ROMFiles/ScriptAction.cs similarity index 100% rename from DS_Map/Script/ScriptAction.cs rename to DS_Map/ROMFiles/ScriptAction.cs diff --git a/DS_Map/ROMFiles/ScriptActionContainer.cs b/DS_Map/ROMFiles/ScriptActionContainer.cs new file mode 100644 index 0000000..ba3f414 --- /dev/null +++ b/DS_Map/ROMFiles/ScriptActionContainer.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace DSPRE.ROMFiles +{ + public class ScriptActionContainer + { + public List commands; + public uint manualUserID; + + public ScriptActionContainer(uint actionNumber, List commands = null) + { + manualUserID = actionNumber; + this.commands = commands; + } + } +} \ No newline at end of file diff --git a/DS_Map/Script/ScriptCommand.cs b/DS_Map/ROMFiles/ScriptCommand.cs similarity index 81% rename from DS_Map/Script/ScriptCommand.cs rename to DS_Map/ROMFiles/ScriptCommand.cs index a21a23f..e176066 100644 --- a/DS_Map/Script/ScriptCommand.cs +++ b/DS_Map/ROMFiles/ScriptCommand.cs @@ -17,17 +17,18 @@ namespace DSPRE.ROMFiles { OW_DIRECTION, FUNCTION_ID, ACTION_ID, - CMD_NUMBER, - LABEL_REF + CMD_NUMBER }; public ushort? id; - public List Parameters { get; set; } = new List(); + public List cmdParams; public string name; // CHANGE: Update the constructor to use ScriptParameter - public ScriptCommand(ushort id, List parameters) { - if (parameters is null) { + public ScriptCommand(ushort id, List parametersList) + { + if (parametersList is null) + { this.id = null; return; } @@ -39,48 +40,48 @@ namespace DSPRE.ROMFiles { switch (id) { case 0x0016: // Jump case 0x001A: // Call - name += $" {FormatParameter(parameters[0], ParamTypeEnum.FUNCTION_ID)}"; + name += $" {FormatNumber(parametersList[0], ParamTypeEnum.FUNCTION_ID)}"; break; case 0x0017: // JumpIfObjID case 0x0018: // JumpIfEventID - name += $" {FormatParameter(parameters[0], ParamTypeEnum.OW_ID)} {FormatParameter(parameters[1])}"; + name += $" {FormatNumber(parametersList[0], ParamTypeEnum.OW_ID)} {FormatNumber(parametersList[1])}"; break; case 0x0019: // JumpIfPlayerDir - name += $" {FormatParameter(parameters[0], ParamTypeEnum.OW_DIRECTION)} {FormatParameter(parameters[1], ParamTypeEnum.FUNCTION_ID)}"; + name += $" {FormatNumber(parametersList[0], ParamTypeEnum.OW_DIRECTION)} {FormatNumber(parametersList[1], ParamTypeEnum.FUNCTION_ID)}"; break; case 0x001C: // JumpIf case 0x001D: // CallIf { - string number = FormatParameter(parameters[1], ParamTypeEnum.FUNCTION_ID); + string number = FormatNumber(parametersList[1], ParamTypeEnum.FUNCTION_ID); // Access the byte value from the parameter's raw data - if (RomInfo.ScriptComparisonOperatorsDict.TryGetValue(parameters[0].RawData[0], out string v)) { + if (RomInfo.ScriptComparisonOperatorsDict.TryGetValue(parametersList[0][0], out string v)) { name += $" {v} {number}"; } else { - name += $" {parameters[0].RawData[0]} {number}"; + name += $" {parametersList[0][0]} {number}"; } break; } case 0x005E: // Movement - name += $" {FormatParameter(parameters[0], ParamTypeEnum.OW_ID)} {FormatParameter(parameters[1], ParamTypeEnum.ACTION_ID)}"; + name += $" {FormatNumber(parametersList[0], ParamTypeEnum.OW_ID)} {FormatNumber(parametersList[1], ParamTypeEnum.ACTION_ID)}"; break; case 0x006A: // GetOverworldPosition - name += FormatCmd_Overworld_TwoParams(parameters); + name += FormatCmd_Overworld_TwoParams(parametersList); break; case 0x0062: // Lock case 0x0063: // Release case 0x0064: // AddOW case 0x0065: // RemoveOW - name += $" {FormatParameter(parameters[0], ParamTypeEnum.OW_ID)}"; + name += $" {FormatNumber(parametersList[0], ParamTypeEnum.OW_ID)}"; break; case 0x006D: // SetOverworldMovement - name += FormatCmd_Overworld_Move(parameters); + name += FormatCmd_Overworld_Move(parametersList); break; case 0x00B0: // Warp [HGSS] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.HGSS)) { - name += FormatCmd_Warp(parameters); + name += FormatCmd_Warp(parametersList); } else { goto default; } @@ -88,7 +89,7 @@ namespace DSPRE.ROMFiles { break; case 0x0152: // SetOverworldDefaultPosition [HGSS] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.HGSS)) { - name += FormatCmd_Overworld_TwoParams(parameters); + name += FormatCmd_Overworld_TwoParams(parametersList); } else { goto default; } @@ -96,7 +97,7 @@ namespace DSPRE.ROMFiles { break; case 0x0153: // SetOverworldPosition [HGSS] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.HGSS)) { - name += FormatCmd_Overworld_3Coords_Dir(parameters); + name += FormatCmd_Overworld_3Coords_Dir(parametersList); } else { goto default; } @@ -104,7 +105,7 @@ namespace DSPRE.ROMFiles { break; case 0x0154: // SetOverworldDefaultMovement [HGSS] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.HGSS)) { - name += FormatCmd_Overworld_Move(parameters); + name += FormatCmd_Overworld_Move(parametersList); } else { goto default; } @@ -112,7 +113,7 @@ namespace DSPRE.ROMFiles { break; case 0x0155: // SetOverworldDefaultDirection [DPPt] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.HGSS)) { - name += FormatCmd_Overworld_Dir(parameters); + name += FormatCmd_Overworld_Dir(parametersList); } else { goto default; } @@ -120,7 +121,7 @@ namespace DSPRE.ROMFiles { break; case 0x0158: // SetOverworldDirection [DPPt] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.HGSS)) { - name += FormatCmd_Overworld_Dir(parameters); + name += FormatCmd_Overworld_Dir(parametersList); } else { goto default; } @@ -129,7 +130,7 @@ namespace DSPRE.ROMFiles { case 0x00BE: // Warp [DPPt] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.DP) || RomInfo.gameFamily.Equals(RomInfo.GameFamilies.Plat)) { - name += FormatCmd_Warp(parameters); + name += FormatCmd_Warp(parametersList); } else { goto default; } @@ -137,7 +138,7 @@ namespace DSPRE.ROMFiles { break; case 0x0186: // SetOverworldDefaultPosition [DPPt] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.DP) || RomInfo.gameFamily.Equals(RomInfo.GameFamilies.Plat)) { - name += FormatCmd_Overworld_TwoParams(parameters); + name += FormatCmd_Overworld_TwoParams(parametersList); } else { goto default; } @@ -145,7 +146,7 @@ namespace DSPRE.ROMFiles { break; case 0x0187: // SetOverworldPosition [DPPt] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.DP) || RomInfo.gameFamily.Equals(RomInfo.GameFamilies.Plat)) { - name += FormatCmd_Overworld_3Coords_Dir(parameters); + name += FormatCmd_Overworld_3Coords_Dir(parametersList); } else { goto default; } @@ -153,7 +154,7 @@ namespace DSPRE.ROMFiles { break; case 0x0188: // SetOverworldDefaultMovement [DPPt] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.DP) || RomInfo.gameFamily.Equals(RomInfo.GameFamilies.Plat)) { - name += FormatCmd_Overworld_Move(parameters); + name += FormatCmd_Overworld_Move(parametersList); } else { goto default; } @@ -161,7 +162,7 @@ namespace DSPRE.ROMFiles { break; case 0x0189: // SetOverworldDefaultDirection [DPPt] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.DP) || RomInfo.gameFamily.Equals(RomInfo.GameFamilies.Plat)) { - name += FormatCmd_Overworld_Dir(parameters); + name += FormatCmd_Overworld_Dir(parametersList); } else { goto default; } @@ -169,7 +170,7 @@ namespace DSPRE.ROMFiles { break; case 0x018C: // SetOverworldDirection [DPPt] if (RomInfo.gameFamily.Equals(RomInfo.GameFamilies.DP) || RomInfo.gameFamily.Equals(RomInfo.GameFamilies.Plat)) { - name += FormatCmd_Overworld_Dir(parameters); + name += FormatCmd_Overworld_Dir(parametersList); } else { goto default; } @@ -177,39 +178,39 @@ namespace DSPRE.ROMFiles { break; default: - for (int i = 0; i < parameters.Count; i++) { - name += $" {FormatParameter(parameters[i])}"; + for (int i = 0; i < parametersList.Count; i++) { + name += $" {FormatNumber(parametersList[i])}"; } break; } this.id = id; - this.Parameters = parameters; + this.cmdParams = parametersList; } - private string FormatCmd_Warp(List parameters) { - return $" {FormatParameter(parameters[0])} {FormatParameter(parameters[1])} {FormatParameter(parameters[2])} {FormatParameter(parameters[3])} {FormatParameter(parameters[4], ParamTypeEnum.OW_DIRECTION)}"; + private string FormatCmd_Warp(List parametersList) { + return $" {FormatNumber(parametersList[0])} {FormatNumber(parametersList[1])} {FormatNumber(parametersList[2])} {FormatNumber(parametersList[3])} {FormatNumber(parametersList[4], ParamTypeEnum.OW_DIRECTION)}"; } - private string FormatCmd_Overworld_TwoParams(List parameters) { - return $" {FormatParameter(parameters[0], ParamTypeEnum.OW_ID)} {FormatParameter(parameters[1])} {FormatParameter(parameters[2])}"; + private string FormatCmd_Overworld_TwoParams(List parametersList) { + return $" {FormatNumber(parametersList[0], ParamTypeEnum.OW_ID)} {FormatNumber(parametersList[1])} {FormatNumber(parametersList[2])}"; } - private string FormatCmd_Overworld_Move(List parameters) { - return $" {FormatParameter(parameters[0], ParamTypeEnum.OW_ID)} {FormatParameter(parameters[1], ParamTypeEnum.OW_MOVEMENT_TYPE)}"; + private string FormatCmd_Overworld_Move(List parametersList) { + return $" {FormatNumber(parametersList[0], ParamTypeEnum.OW_ID)} {FormatNumber(parametersList[1], ParamTypeEnum.OW_MOVEMENT_TYPE)}"; } - private string FormatCmd_Overworld_3Coords_Dir(List parameters) { - return $" {FormatParameter(parameters[0], ParamTypeEnum.OW_ID)} {FormatParameter(parameters[1])} {FormatParameter(parameters[2])} {FormatParameter(parameters[3])} {FormatParameter(parameters[4], ParamTypeEnum.OW_DIRECTION)}"; + private string FormatCmd_Overworld_3Coords_Dir(List parametersList) { + return $" {FormatNumber(parametersList[0], ParamTypeEnum.OW_ID)} {FormatNumber(parametersList[1])} {FormatNumber(parametersList[2])} {FormatNumber(parametersList[3])} {FormatNumber(parametersList[4], ParamTypeEnum.OW_DIRECTION)}"; } - private string FormatCmd_Overworld_Dir(List parameters) { - return $" {FormatParameter(parameters[0], ParamTypeEnum.OW_ID)} {FormatParameter(parameters[1], ParamTypeEnum.OW_DIRECTION)}"; + private string FormatCmd_Overworld_Dir(List parametersList) { + return $" {FormatNumber(parametersList[0], ParamTypeEnum.OW_ID)} {FormatNumber(parametersList[1], ParamTypeEnum.OW_DIRECTION)}"; } public ScriptCommand(string wholeLine, int lineNumber = 0) { name = wholeLine; - Parameters = new List(); + cmdParams = new List(); string[] nameParts = wholeLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); // Separate command code from parameters /* Get command id, which is always first in the description */ @@ -244,7 +245,7 @@ namespace DSPRE.ROMFiles { int firstParamValue = int.Parse(nameParts[1].PurgeSpecial(ScriptFile.specialChars), nameParts[1].GetNumberStyle()); byte firstParamSize = parametersSizeArr[1]; - Parameters.Add(new ScriptParameter(firstParamValue.ToByteArrayChooseSize(firstParamSize))); + cmdParams.Add(firstParamValue.ToByteArrayChooseSize(firstParamSize)); paramsProcessed++; int i = 2; @@ -306,8 +307,7 @@ namespace DSPRE.ROMFiles { Console.WriteLine($"Parameter #{i}: {nameParts[i + 1]}"); if (RomInfo.ScriptComparisonOperatorsReverseDict.TryGetValue(nameParts[i + 1].ToLower(), out cmdID)) { //Check succeeds when command is like "asdfg LESS" or "asdfg DIFFERENT" - // Create parameter with byte array - Parameters.Add(new ScriptParameter(new byte[] { (byte)cmdID })); + cmdParams.Add(new byte[] { (byte)cmdID }); } else { //Not a comparison /* Convert strings of parameters to the correct datatypes */ NumberStyles numStyle = nameParts[i + 1].GetNumberStyle(); @@ -316,16 +316,6 @@ namespace DSPRE.ROMFiles { int result = 0; try { - // Check if this is a label reference - if (nameParts[i + 1].StartsWith("label") || nameParts[i + 1].StartsWith("script")) { - // This is a label reference - ScriptParameter labelParam = new ScriptParameter(0, nameParts[i + 1]) { - Type = ScriptParameter.ParameterType.RelativeJump - }; - Parameters.Add(labelParam); - continue; - } - result = int.Parse(nameParts[i + 1], numStyle); } catch { if (string.IsNullOrWhiteSpace(nameParts[i + 1])) { @@ -352,8 +342,7 @@ namespace DSPRE.ROMFiles { } try { - byte[] paramData = result.ToByteArrayChooseSize(parametersSizeArr[i]); - Parameters.Add(new ScriptParameter(paramData)); + cmdParams.Add(result.ToByteArrayChooseSize(parametersSizeArr[i])); } catch (OverflowException) { MessageBox.Show($"Argument {nameParts[i + 1]} at line {lineNumber} is not in the range [0, {Math.Pow(2, 8 * parametersSizeArr[i]) - 1}].", "Argument error", MessageBoxButtons.OK, MessageBoxIcon.Error); id = null; @@ -369,10 +358,6 @@ namespace DSPRE.ROMFiles { } private string FormatNumber(byte[] par, ParamTypeEnum paramType = ParamTypeEnum.INTEGER) { - if (paramType == ParamTypeEnum.LABEL_REF && par.Length > 0) { - return FormatLabelReference(Encoding.ASCII.GetString(par)); - } - //number acquisition uint num; if (par.Length == 0) { @@ -409,6 +394,9 @@ namespace DSPRE.ROMFiles { case ParamTypeEnum.CMD_NUMBER: return "CMD_" + prefix + num.ToString(formatOverride + '3'); + case ParamTypeEnum.FUNCTION_ID: + return ScriptFile.ContainerTypes.Function.ToString() + "#" + num; + case ParamTypeEnum.ACTION_ID: return ScriptFile.ContainerTypes.Action.ToString() + "#" + num; diff --git a/DS_Map/ROMFiles/ScriptCommandContainer.cs b/DS_Map/ROMFiles/ScriptCommandContainer.cs new file mode 100644 index 0000000..3b1793e --- /dev/null +++ b/DS_Map/ROMFiles/ScriptCommandContainer.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace DSPRE.ROMFiles +{ + public class ScriptCommandContainer + { + public List commands; + public uint manualUserID; + public int usedScriptID; //useScript ID referenced by this Script/Function + public ScriptFile.ContainerTypes containerType; + internal static readonly string functionStart; + + public ScriptCommandContainer(uint scriptNumber, ScriptFile.ContainerTypes containerType, int usedScriptID = -1, List commandList = null) + { + manualUserID = scriptNumber; + this.usedScriptID = usedScriptID; + this.containerType = containerType; + commands = commandList; + } + + public ScriptCommandContainer(uint newID, ScriptCommandContainer toCopy) + { + manualUserID = newID; + usedScriptID = toCopy.usedScriptID; + containerType = toCopy.containerType; + commands = new List(toCopy.commands); //command parameters need to be copied recursively + } + } +} \ No newline at end of file diff --git a/DS_Map/ROMFiles/ScriptFile.cs b/DS_Map/ROMFiles/ScriptFile.cs index 8f2d633..22a445c 100644 --- a/DS_Map/ROMFiles/ScriptFile.cs +++ b/DS_Map/ROMFiles/ScriptFile.cs @@ -1,3 +1,4 @@ +using DSPRE.Resources; using System; using System.Collections.Generic; using System.IO; @@ -13,13 +14,11 @@ namespace DSPRE.ROMFiles /// public class ScriptFile : RomFile { - public List CommandSequence { get; set; } = new List(); - public Dictionary OffsetToLabelMap { get; set; } = new Dictionary(); public enum ContainerTypes { + Function, Action, - Script, - Label + Script }; public struct ContainerReference @@ -28,28 +27,29 @@ namespace DSPRE.ROMFiles public uint offsetInFile; } + public List allScripts = new List(); + public List allFunctions = new List(); + public List allActions = new List(); public int fileID = -1; public bool isLevelScript = new bool(); - public bool HasNoScripts { get { return fileID == int.MaxValue; } } + public bool hasNoScripts { get { return fileID == int.MaxValue; } } public static readonly char[] specialChars = { 'x', 'X', '#', '.', '_' }; - public ScriptFile(Stream fs) + public ScriptFile(Stream fs, bool readFunctions = true, bool readActions = true) { - // Initialize collections - CommandSequence = new List(); - OffsetToLabelMap = new Dictionary(); + List scriptOffsets = new List(); + List functionOffsets = new List(); + List movementOffsets = new List(); using (BinaryReader br = new BinaryReader(fs)) { - // Read header to find entry points - List entryPointOffsets = new List(); - isLevelScript = true; // Is Level Script until proved otherwise + /* Read script offsets from the header */ + isLevelScript = true; // Is Level Script as long as magic number FD13 doesn't exist try { - int entryPointIndex = 0; while (true) { long headerPos = br.BaseStream.Position; @@ -57,7 +57,7 @@ namespace DSPRE.ROMFiles br.BaseStream.Position -= 0x2; uint value = br.ReadUInt32(); - if (value == 0 && entryPointOffsets.Count == 0) + if (value == 0 && scriptOffsets.Count == 0) { isLevelScript = true; break; @@ -70,13 +70,8 @@ namespace DSPRE.ROMFiles break; } - int offsetFromStart = (int)(value + headerPos + 4); - entryPointOffsets.Add(offsetFromStart); - - // Create entry point label - string entryLabel = $"script_{entryPointIndex}"; - OffsetToLabelMap[offsetFromStart] = entryLabel; - entryPointIndex++; + int offsetFromStart = (int)(value + br.BaseStream.Position); // Don't change order of addition + scriptOffsets.Add(offsetFromStart); } } catch (EndOfStreamException) @@ -94,488 +89,673 @@ namespace DSPRE.ROMFiles // Skip the 0xFD13 marker br.ReadUInt16(); - - // Process commands until end of file - while (br.BaseStream.Position < br.BaseStream.Length) + /* Read scripts */ + for (uint current = 0; current < scriptOffsets.Count; current++) { - int currentOffset = (int)br.BaseStream.Position; + int index = scriptOffsets.FindIndex(x => x == scriptOffsets[(int)current]); // Check for UseScript - // Sometimes we might hit padding bytes - try to detect and skip - if (br.BaseStream.Position + 2 <= br.BaseStream.Length) + if (index == current) { - byte[] peekBytes = br.ReadBytes(2); - br.BaseStream.Position -= 2; + br.BaseStream.Position = scriptOffsets[(int)current]; - // Check if these look like padding - if (peekBytes[0] == 0 && peekBytes[1] == 0) + List cmdList = new List(); + bool endScript = new bool(); + while (!endScript) { - // Skip padding byte - br.ReadByte(); - continue; + ScriptCommand cmd = ReadCommand(br, ref functionOffsets, ref movementOffsets); + if (cmd.cmdParams is null) + { + return; + } + + cmdList.Add(cmd); + + if (ScriptDatabase.endCodes.Contains((ushort)cmd.id)) + { + endScript = true; + } + } + + allScripts.Add(new ScriptCommandContainer(current + 1, ContainerTypes.Script, commandList: cmdList)); + } + else + { + allScripts.Add(new ScriptCommandContainer(current + 1, ContainerTypes.Script, usedScriptID: index + 1)); + } + } + + /* Read functions */ + if (readFunctions) + { + for (uint current = 0; current < functionOffsets.Count; current++) + { + br.BaseStream.Position = functionOffsets[(int)current]; + int posInList = scriptOffsets.IndexOf(functionOffsets[(int)current]); // Check for UseScript + + if (posInList == -1) + { + List cmdList = new List(); + bool endFunction = new bool(); + while (!endFunction) + { + ScriptCommand command = ReadCommand(br, ref functionOffsets, ref movementOffsets); + if (command.cmdParams is null) + { + return; + } + + cmdList.Add(command); + if (ScriptDatabase.endCodes.Contains((ushort)command.id)) + { + endFunction = true; + } + } + + allFunctions.Add(new ScriptCommandContainer(current + 1, ContainerTypes.Function, commandList: cmdList)); + } + else + { + allFunctions.Add(new ScriptCommandContainer(current + 1, ContainerTypes.Function, usedScriptID: posInList + 1)); } } + } - ScriptCommand cmd = ReadCommand(br); - if (cmd == null || cmd.id == null) + if (readActions) + { + /* Read movements */ + for (uint current = 0; current < movementOffsets.Count; current++) { - break; // End of file or error - } + br.BaseStream.Position = movementOffsets[(int)current]; - // Check if this is an entry point or jump target - bool isEntryPoint = false; - int entryPointIndex = -1; - string label = null; - - if (OffsetToLabelMap.TryGetValue(currentOffset, out string existingLabel)) - { - label = existingLabel; - isEntryPoint = existingLabel.StartsWith("script_"); - if (isEntryPoint) + List cmdList = new List(); + bool endMovement = new bool(); + while (!endMovement) { - entryPointIndex = int.Parse(existingLabel.Substring("script_".Length)); + ushort id = br.ReadUInt16(); + if (id == 0xFE) + { + endMovement = true; + cmdList.Add(new ScriptAction(id, 0)); + } + else + { + cmdList.Add(new ScriptAction(id, br.ReadUInt16())); + } } - } - // Add to command sequence - CommandSequence.Add(new ScriptCommandPosition( - cmd, currentOffset, label, isEntryPoint, entryPointIndex)); + allActions.Add(new ScriptActionContainer(current + 1, commands: cmdList)); + } } } } - public ScriptFile(int fileID) : this(getFileStream(fileID)) + public ScriptFile(int fileID, bool readFunctions = true, bool readActions = true) : this(getFileStream(fileID), readFunctions, readActions) { this.fileID = fileID; } - public static FileStream getFileStream(int fileID) + static FileStream getFileStream(int fileID) { string path = Filesystem.GetScriptPath(fileID); return new FileStream(path, FileMode.OpenOrCreate); } - public override string ToString() { string prefix = isLevelScript ? "Level " : ""; return $"{prefix}Script File " + this.fileID; } - public ScriptFile(IEnumerable lines, int fileID = -1) + public ScriptFile(List scripts, List functions, List movements, int fileID = -1) { - CommandSequence = new List(); - OffsetToLabelMap = new Dictionary(); - this.fileID = fileID; + allScripts = scripts; + allFunctions = functions; + allActions = movements; + isLevelScript = false; + } - int currentOffset = 0; - string currentLabel = null; - int entryPointIndex = 0; - bool isCurrentLabelEntryPoint = false; // Track this instead - - // Parse each line - foreach (var line in lines) + public ScriptFile(IEnumerable scriptLines, IEnumerable functionLines, IEnumerable actionLines, int fileID = -1) + { + //TODO: give user the possibility to jump to/call a script + //once it's done, this Predicate below will be the only one needed, since there will be no distinction between + //a script and a function + bool functionEndCondition(List<(int linenum, string text)> source, int x, ushort? id) { - string trimmedLine = line.Trim(); + return source[x].text.TrimEnd().IgnoreCaseEquals(RomInfo.ScriptCommandNamesDict[0x0002]) //End + || source[x].text.IndexOf(RomInfo.ScriptCommandNamesDict[0x0016] + ' ' + ContainerTypes.Function.ToString(), StringComparison.InvariantCultureIgnoreCase) >= 0 //Jump Function_# + || source[x].text.TrimEnd().IgnoreCaseEquals(RomInfo.ScriptCommandNamesDict[0x001B]) + || ScriptDatabase.endCodes.Contains(id); + } //Return - // Skip empty lines - if (string.IsNullOrWhiteSpace(trimmedLine)) + bool scriptEndCondition(List<(int linenum, string text)> source, int x, ushort? id) + { + return source[x].text.TrimEnd().IgnoreCaseEquals(RomInfo.ScriptCommandNamesDict[0x0002]) //End + || source[x].text.IndexOf(RomInfo.ScriptCommandNamesDict[0x0016] + ' ' + ContainerTypes.Function.ToString()) >= 0 //Jump Function_# + || ScriptDatabase.endCodes.Contains(id); + } + + allScripts = ReadCommandsFromLines(scriptLines.ToList(), ContainerTypes.Script, scriptEndCondition); //Jump + whitespace + if (allScripts is null) + { + return; + } + + if (allScripts.Count <= 0) + { + this.fileID = int.MaxValue; + return; + } + + if (functionLines != null) + { + allFunctions = ReadCommandsFromLines(functionLines.ToList(), ContainerTypes.Function, functionEndCondition); //Jump + whitespace + if (allFunctions is null) { - continue; + return; } + } - // Check if this is a label - if (trimmedLine.EndsWith(":")) + if (actionLines != null) + { + allActions = ReadActionsFromLines(actionLines.ToList()); + if (allActions is null) { - currentLabel = trimmedLine.Substring(0, trimmedLine.Length - 1); + return; + } + } - // Track if it's an entry point - isCurrentLabelEntryPoint = currentLabel.StartsWith("script_"); - if (isCurrentLabelEntryPoint) + this.fileID = fileID; + } + + private ScriptCommand ReadCommand(BinaryReader dataReader, ref List functionOffsets, ref List actionOffsets) + { + ushort id = dataReader.ReadUInt16(); + List parameterList = new List(); + + /* How to read parameters for different commands for DPPt*/ + switch (RomInfo.gameFamily) + { + case GameFamilies.DP: + case GameFamilies.Plat: + switch (id) { - if (int.TryParse(currentLabel.Substring("script_".Length), out int index)) - { - entryPointIndex = index; - } + case 0x16: //Jump + case 0x1A: //Call + ProcessRelativeJump(dataReader, ref parameterList, ref functionOffsets); + break; + case 0x17: //JumpIfObjID + case 0x18: //JumpIfBgID + case 0x19: //JumpIfPlayerDir + case 0x1C: //JumpIf + case 0x1D: //CallIf + //in the case of JumpIf and CallIf, the first param is a comparisonOperator + //for JumpIfPlayerDir it's a directionID + //for JumpIfObjID, it's an EventID + parameterList.Add(new byte[] { dataReader.ReadByte() }); + ProcessRelativeJump(dataReader, ref parameterList, ref functionOffsets); + break; + case 0x5E: // Movement + parameterList.Add(BitConverter.GetBytes(dataReader.ReadUInt16())); + ProcessRelativeJump(dataReader, ref parameterList, ref actionOffsets); + break; + case 0x1CF: + case 0x1D0: + case 0x1D1: + { + byte parameter1 = dataReader.ReadByte(); + parameterList.Add(new byte[] { parameter1 }); + if (parameter1 == 0x2) + { + parameterList.Add(dataReader.ReadBytes(2)); //Read additional u16 if first param read is 2 + } + } + break; + case 0x21D: + { + ushort parameter1 = dataReader.ReadUInt16(); + parameterList.Add(BitConverter.GetBytes(parameter1)); + + switch (parameter1) + { + case 0: + case 1: + case 2: + case 3: + parameterList.Add(dataReader.ReadBytes(2)); + parameterList.Add(dataReader.ReadBytes(2)); + break; + case 4: + case 5: + parameterList.Add(dataReader.ReadBytes(2)); + break; + case 6: + break; + } + } + break; + case 0x235: + { + short parameter1 = dataReader.ReadInt16(); + parameterList.Add(BitConverter.GetBytes(parameter1)); + + switch (parameter1) + { + case 0x1: + case 0x3: + parameterList.Add(dataReader.ReadBytes(2)); + parameterList.Add(dataReader.ReadBytes(2)); + parameterList.Add(dataReader.ReadBytes(2)); + break; + case 0x4: + parameterList.Add(dataReader.ReadBytes(2)); + parameterList.Add(dataReader.ReadBytes(2)); + break; + case 0x0: + case 0x6: + parameterList.Add(dataReader.ReadBytes(2)); + break; + default: + break; + } + } + break; + case 0x23E: + { + short parameter1 = dataReader.ReadInt16(); + parameterList.Add(BitConverter.GetBytes(parameter1)); + + switch (parameter1) + { + case 0x1: + case 0x3: + parameterList.Add(dataReader.ReadBytes(2)); + break; + case 0x5: + case 0x6: + parameterList.Add(dataReader.ReadBytes(2)); + parameterList.Add(dataReader.ReadBytes(2)); + break; + default: + break; + } + } + break; + case 0x2C4: + { + byte parameter1 = dataReader.ReadByte(); + parameterList.Add(new byte[] { parameter1 }); + if (parameter1 == 0 || parameter1 == 1) + { + parameterList.Add(dataReader.ReadBytes(2)); + } + } + break; + case 0x2C5: + { + if (RomInfo.gameVersion == GameVersions.Platinum) + { + parameterList.Add(dataReader.ReadBytes(2)); + parameterList.Add(dataReader.ReadBytes(2)); + } + else + { + goto default; + } + } + break; + case 0x2C6: + case 0x2C9: + case 0x2CA: + case 0x2CD: + if (RomInfo.gameVersion == GameVersions.Platinum) + { + break; + } + else + { + goto default; + } + case 0x2CF: + if (RomInfo.gameVersion == GameVersions.Platinum) + { + parameterList.Add(dataReader.ReadBytes(2)); + parameterList.Add(dataReader.ReadBytes(2)); + } + else + { + goto default; + } + + break; + default: + addParametersToList(ref parameterList, id, dataReader); + break; } - continue; - } + break; + case GameFamilies.HGSS: + switch (id) + { + case 0x16: //Jump + case 0x1A: //Call + ProcessRelativeJump(dataReader, ref parameterList, ref functionOffsets); + break; + case 0x17: //JumpIfObjID + case 0x18: //JumpIfBgID + case 0x19: //JumpIfPlayerDir + case 0x1C: //JumpIf + case 0x1D: //CallIf + parameterList.Add(new byte[] { dataReader.ReadByte() }); //in the case of JumpIf and CallIf, the first param is a comparisonOperator + ProcessRelativeJump(dataReader, ref parameterList, ref functionOffsets); + break; + case 0x5E: // Movement + parameterList.Add(BitConverter.GetBytes(dataReader.ReadUInt16())); //in the case of Movement, the first param is an overworld ID + ProcessRelativeJump(dataReader, ref parameterList, ref actionOffsets); + break; + case 0x190: + case 0x191: + case 0x192: + { + byte parameter1 = dataReader.ReadByte(); + parameterList.Add(new byte[] { parameter1 }); + if (parameter1 == 0x2) + { + parameterList.Add(dataReader.ReadBytes(2)); + } + } + break; + case 0x1D1: // Number of parameters differ depending on the first parameter value + { + short parameter1 = dataReader.ReadInt16(); + parameterList.Add(BitConverter.GetBytes(parameter1)); + switch (parameter1) + { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + parameterList.Add(dataReader.ReadBytes(2)); + parameterList.Add(dataReader.ReadBytes(2)); + break; + case 0x4: + case 0x5: + parameterList.Add(dataReader.ReadBytes(2)); + break; + case 0x6: + break; + case 0x7: + parameterList.Add(dataReader.ReadBytes(2)); + break; + default: + break; + } + } + break; + case 0x1E9: // Number of parameters differ depending on the first parameter value + { + short parameter1 = dataReader.ReadInt16(); + parameterList.Add(BitConverter.GetBytes(parameter1)); + switch (parameter1) + { + case 0x0: + break; + case 0x1: + case 0x2: + case 0x3: + parameterList.Add(dataReader.ReadBytes(2)); + break; + case 0x4: + break; + case 0x5: + case 0x6: + parameterList.Add(dataReader.ReadBytes(2)); + parameterList.Add(dataReader.ReadBytes(2)); + break; + case 0x7: + case 0x8: + break; + default: + break; + } + } + break; + default: + addParametersToList(ref parameterList, id, dataReader); + break; + } - // Parse the command - ScriptCommand cmd = new ScriptCommand(trimmedLine); - if (cmd.id == null) + break; + } + + return new ScriptCommand(id, parameterList); + } + + private void ProcessRelativeJump(BinaryReader dataReader, ref List parameterList, ref List offsetsList) + { + int relativeOffset = dataReader.ReadInt32(); + int offsetFromScriptFileStart = (int)(relativeOffset + dataReader.BaseStream.Position); + + if (!offsetsList.Contains(offsetFromScriptFileStart)) + { + offsetsList.Add(offsetFromScriptFileStart); + } + + int functionNumber = offsetsList.IndexOf(offsetFromScriptFileStart); + if (functionNumber < 0) + { + throw new InvalidOperationException(); + } + + parameterList.Add(BitConverter.GetBytes(functionNumber + 1)); + } + + private void addParametersToList(ref List parameterList, ushort id, BinaryReader dataReader) + { + Console.WriteLine("Loaded command id: " + id.ToString("X4")); + try + { + foreach (int bytesToRead in RomInfo.ScriptCommandParametersDict[id]) { - continue; // Skip invalid commands + parameterList.Add(dataReader.ReadBytes(bytesToRead)); } - - // Calculate the size of this command for offset tracking - int cmdSize = 2; // Command ID (2 bytes) - foreach (var param in cmd.Parameters) - { - cmdSize += param.RawData.Length; - } - - // Use the tracked entry point flag - int epIndex = isCurrentLabelEntryPoint ? entryPointIndex : -1; - - CommandSequence.Add(new ScriptCommandPosition( - cmd, currentOffset, currentLabel, isCurrentLabelEntryPoint, epIndex)); - - // Map the offset to the label for jump targets - if (currentLabel != null) - { - OffsetToLabelMap[currentOffset] = currentLabel; - currentLabel = null; // Reset label - isCurrentLabelEntryPoint = false; // Reset entry point flag - } - - // Update offset for next command - currentOffset += cmdSize; + } + catch (NullReferenceException) + { + MessageBox.Show("Script command " + id + "can't be handled for now." + + Environment.NewLine + "Reference offset 0x" + dataReader.BaseStream.Position.ToString("X"), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + parameterList = null; + return; + } + catch + { + MessageBox.Show("Error: ID Read - " + id + + Environment.NewLine + "Reference offset 0x" + dataReader.BaseStream.Position.ToString("X"), "Unrecognized script command", MessageBoxButtons.OK, MessageBoxIcon.Error); + parameterList = null; + return; } } - private ScriptCommand ReadCommand(BinaryReader br) + private void AddReference(ref List references, ushort commandID, List parameterList, int pos, ScriptCommandContainer cont) { - // Check if we've reached the end of the file - if (br.BaseStream.Position >= br.BaseStream.Length) + if (ScriptDatabase.commandsWithRelativeJump.TryGetValue(commandID, out int parameterWithRelativeJump)) { - return null; + uint invokedID = BitConverter.ToUInt32(parameterList[parameterWithRelativeJump], 0); // Jump, Call + + if (commandID == 0x005E) + references.Add(new ScriptReference(cont.containerType, cont.manualUserID, ContainerTypes.Action, invokedID, pos - 4)); + else + { + references.Add(new ScriptReference(cont.containerType, cont.manualUserID, ContainerTypes.Function, invokedID, pos - 4)); + } } + } + + private List ReadCommandsFromLines(List linelist, ContainerTypes containerType, Func, int, ushort?, bool> endConditions) + { + List<(int linenum, string text)> lineSource = new List<(int linenum, string text)>(); + + for (int l = 0; l < linelist.Count; l++) + { + string cur = linelist[l]; + if (!string.IsNullOrWhiteSpace(cur)) + { + lineSource.Add((l, cur)); + } + } + + List ls = new List(); + int i = 0; try { - ushort id = br.ReadUInt16(); - List parameters = new List(); + uint scriptNumber = 0; - // Track the original position for jump calculations - long commandStartPos = br.BaseStream.Position - 2; - - switch (gameFamily) + while (i < lineSource.Count) { - case GameFamilies.Plat: - switch (id) + if (scriptNumber == 0) + { + int positionOfScriptNumber; + int positionOfScriptKeyword = lineSource[i].text.IndexOf(containerType.ToString(), StringComparison.InvariantCultureIgnoreCase); + + if (positionOfScriptKeyword > 0) { - case 0x16: // Jump - case 0x1A: // Call - ProcessRelativeJumpLinear(br, parameters); - break; - - case 0x17: // JumpIfObjID - case 0x18: // JumpIfEventID - case 0x19: // JumpIfPlayerDir - case 0x1C: // JumpIf - case 0x1D: // CallIf - // First parameter (condition) - parameters.Add(new ScriptParameter(new byte[] { br.ReadByte() })); - // Then jump target - ProcessRelativeJumpLinear(br, parameters); - break; - - case 0x5E: // Movement - parameters.Add(new ScriptParameter(BitConverter.GetBytes(br.ReadUInt16()))); - ProcessRelativeJumpLinear(br, parameters); - break; - - case 0x1CF: - case 0x1D0: - case 0x1D1: - { - byte parameter1 = br.ReadByte(); - parameters.Add(new ScriptParameter(new byte[] { parameter1 })); - if (parameter1 == 0x2) - { - parameters.Add(new ScriptParameter(br.ReadBytes(2))); //Read additional u16 if first param read is 2 - } - } - break; - case 0x21D: - { - ushort parameter1 = br.ReadUInt16(); - parameters.Add(new ScriptParameter(BitConverter.GetBytes(parameter1))); - - switch (parameter1) - { - case 0: - case 1: - case 2: - case 3: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - case 4: - case 5: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - case 6: - break; - } - } - break; - case 0x235: - { - short parameter1 = br.ReadInt16(); - parameters.Add(new ScriptParameter(BitConverter.GetBytes(parameter1))); - - switch (parameter1) - { - case 0x1: - case 0x3: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - case 0x4: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - case 0x0: - case 0x6: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - default: - break; - } - } - break; - case 0x23E: - { - short parameter1 = br.ReadInt16(); - parameters.Add(new ScriptParameter(BitConverter.GetBytes(parameter1))); - - switch (parameter1) - { - case 0x1: - case 0x3: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - case 0x5: - case 0x6: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - default: - break; - } - } - break; - case 0x2C4: - { - byte parameter1 = br.ReadByte(); - parameters.Add(new ScriptParameter(new byte[] { parameter1 })); - if (parameter1 == 0 || parameter1 == 1) - { - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - } - } - break; - case 0x2C5: - { - if (RomInfo.gameVersion == GameVersions.Platinum) - { - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - } - else - { - goto default; - } - } - break; - case 0x2C6: - case 0x2C9: - case 0x2CA: - case 0x2CD: - if (RomInfo.gameVersion == GameVersions.Platinum) - { - break; - } - else - { - goto default; - } - case 0x2CF: - if (RomInfo.gameVersion == GameVersions.Platinum) - { - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - } - else - { - goto default; - } - - break; - default: - // Standard command handling - if (RomInfo.ScriptCommandParametersDict.TryGetValue(id, out byte[] paramSizes)) - { - foreach (int size in paramSizes) - { - parameters.Add(new ScriptParameter(br.ReadBytes(size))); - } - } - break; + MessageBox.Show("Unrecognized container keyword: \"" + lineSource[i] + '"', "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return null; } - break; - - case GameFamilies.HGSS: - switch (id) + else if (positionOfScriptKeyword < 0) { - case 0x16: //Jump - case 0x1A: //Call - ProcessRelativeJumpLinear(br, parameters); - break; - - case 0x17: //JumpIfObjID - case 0x18: //JumpIfBgID - case 0x19: //JumpIfPlayerDir - case 0x1C: //JumpIf - case 0x1D: //CallIf - parameters.Add(new ScriptParameter(new byte[] { br.ReadByte() })); //in the case of JumpIf and CallIf, the first param is a comparisonOperator - ProcessRelativeJumpLinear(br, parameters); - break; - - case 0x5E: // Movement - parameters.Add(new ScriptParameter(BitConverter.GetBytes(br.ReadUInt16()))); //in the case of Movement, the first param is an overworld ID - ProcessRelativeJumpLinear(br, parameters); - break; - - case 0x190: - case 0x191: - case 0x192: - { - byte parameter1 = br.ReadByte(); - parameters.Add(new ScriptParameter(new byte[] { parameter1 })); - if (parameter1 == 0x2) - { - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - } - } - break; - - case 0x1D1: // Number of parameters differ depending on the first parameter value - { - short parameter1 = br.ReadInt16(); - parameters.Add(new ScriptParameter(BitConverter.GetBytes(parameter1))); - switch (parameter1) - { - case 0x0: - case 0x1: - case 0x2: - case 0x3: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - case 0x4: - case 0x5: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - case 0x6: - break; - case 0x7: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - default: - break; - } - } - break; - - case 0x1E9: // Number of parameters differ depending on the first parameter value - { - short parameter1 = br.ReadInt16(); - parameters.Add(new ScriptParameter(BitConverter.GetBytes(parameter1))); - switch (parameter1) - { - case 0x0: - break; - case 0x1: - case 0x2: - case 0x3: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - case 0x4: - break; - case 0x5: - case 0x6: - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - parameters.Add(new ScriptParameter(br.ReadBytes(2))); - break; - case 0x7: - case 0x8: - break; - default: - break; - } - } - break; - - default: - // For standard commands, read parameters based on definition - if (RomInfo.ScriptCommandParametersDict.TryGetValue(id, out byte[] paramSizes)) - { - foreach (int size in paramSizes) - { - parameters.Add(new ScriptParameter(br.ReadBytes(size))); - } - } - break; + i++; + continue; + } + else + { + if ((positionOfScriptNumber = lineSource[i].text.IndexOfFirstNumber()) < positionOfScriptKeyword) + { + MessageBox.Show("Unspecified Script/Function label.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return null; + } } - break; + scriptNumber = uint.Parse(lineSource[i++].text.Substring(positionOfScriptNumber).Split()[0].Replace(":", "")); + } + + if (lineSource[i].text.IndexOf("UseScript", StringComparison.InvariantCultureIgnoreCase) >= 0) + { + int useScriptNumber = short.Parse(lineSource[i].text.Substring(1 + lineSource[i].text.IndexOf('#'))); + ls.Add(new ScriptCommandContainer(scriptNumber, containerType, useScriptNumber)); + i++; + } + else + { + /* Read script commands */ + List cmdList = new List(); + ScriptCommand lastRead; + + do + { + lastRead = new ScriptCommand(lineSource[i].text, lineSource[i].linenum + 1); + if (lastRead.id is null) + { + return null; + } + + cmdList.Add(lastRead); + } + while (!endConditions(lineSource, i++, lastRead.id)); + + ls.Add(new ScriptCommandContainer(scriptNumber, containerType, commandList: cmdList)); + } + + scriptNumber = 0; } - - return new ScriptCommand(id, parameters); } - catch (Exception ex) + catch (ArgumentOutOfRangeException) { - Console.WriteLine($"Error reading command at offset {br.BaseStream.Position}: {ex.Message}"); + MessageBox.Show($"Unexpectedly reached end of lines.\n\n" + + $"Last line index: {lineSource[i].linenum}.\n" + + $"Managed to parse {ls.Count} Command Containers.", "Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return null; } + + return ls; } - private void ProcessRelativeJumpLinear(BinaryReader br, List parameters) + private List ReadActionsFromLines(List linelist) { - // Read the relative offset - int relativeOffset = br.ReadInt32(); + List<(int linenum, string text)> lineSource = new List<(int linenum, string text)>(); - // Calculate absolute target - int targetOffset = (int)(relativeOffset + br.BaseStream.Position); - - // Add to label map if not already there - if (!OffsetToLabelMap.ContainsKey(targetOffset)) + for (int l = 0; l < linelist.Count; l++) { - string labelName = $"label_0x{targetOffset:X}"; - OffsetToLabelMap[targetOffset] = labelName; - } - - // Create jump parameter - string targetLabel = OffsetToLabelMap[targetOffset]; - ScriptParameter jumpParam = new ScriptParameter(relativeOffset, targetLabel) - { - Type = ScriptParameter.ParameterType.RelativeJump - }; - parameters.Add(jumpParam); - } - - // Convert to text - outputs commands in the order they appear in the binary - public string ToText() - { - StringBuilder sb = new StringBuilder(); - - foreach (var cmdPos in CommandSequence) - { - // If this command needs a label, output it - if (!string.IsNullOrEmpty(cmdPos.Label)) + string cur = linelist[l]; + if (!string.IsNullOrWhiteSpace(cur)) { - sb.AppendLine($"\n{cmdPos.Label}:"); + lineSource.Add((l, cur)); } - - // Output the command (indented if not an entry point) - string indent = "\t"; - sb.AppendLine($"{indent}{cmdPos.Command.name}"); } - return sb.ToString(); + List ls = new List(); + int i = 0; + + try + { + uint actionNumber = 0; + + while (i < lineSource.Count) + { + if (actionNumber == 0) + { + int positionOfActionNumber; + int positionOfActionKeyword = lineSource[i].text.IndexOf(ContainerTypes.Action.ToString(), StringComparison.InvariantCultureIgnoreCase); + + if (positionOfActionKeyword > 0) + { + MessageBox.Show("Unrecognized container keyword: \"" + lineSource[i] + '"', "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return null; + } + else if (positionOfActionKeyword < 0) + { + i++; + continue; + } + else + { + if ((positionOfActionNumber = lineSource[i].text.IndexOfFirstNumber()) < positionOfActionKeyword) + { + MessageBox.Show("Unspecified Action label.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return null; + } + } + + actionNumber = uint.Parse(lineSource[i].text.Substring(positionOfActionNumber).Split()[0].Replace(":", "")); + i++; + } + + List cmdList = new List(); + /* Read script actions */ + do + { + ScriptAction toAdd = new ScriptAction(lineSource[i].text, lineSource[i].linenum + 1); + if (toAdd.id is null) + { + return null; + } + + cmdList.Add(toAdd); + } + while (!lineSource[i++].text.IgnoreCaseEquals(RomInfo.ScriptActionNamesDict[0x00FE])); + + ls.Add(new ScriptActionContainer(actionNumber, commands: cmdList)); + actionNumber = 0; + } + } + catch (ArgumentOutOfRangeException) + { + MessageBox.Show($"Unexpectedly reached end of lines.\n\n" + + $"Last line index: {i}.\n" + + $"Managed to parse {ls.Count} Command Containers.", "Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return null; + } + + return ls; } public override byte[] ToByteArray() @@ -583,83 +763,315 @@ namespace DSPRE.ROMFiles MemoryStream newData = new MemoryStream(); using (BinaryWriter writer = new BinaryWriter(newData)) { - // First, find all entry points and their positions in the command sequence - var entryPoints = CommandSequence - .Where(c => c.IsEntryPoint) - .OrderBy(c => c.EntryPointIndex) - .ToList(); + List scriptOffsets = new List(); //uint OFFSET, int Function/Script/Action ID + List functionOffsets = new List(); + List actionOffsets = new List(); - // Allocate space for header - long headerStart = writer.BaseStream.Position; - writer.BaseStream.Position += entryPoints.Count * 4; - writer.Write((ushort)0xFD13); // End of header marker + List refList = new List(); - // Keep track of command offsets in the new file - Dictionary oldOffsetToNewOffset = new Dictionary(); - - // Write all commands sequentially - foreach (var cmdPos in CommandSequence) + /* Allocate enough space for script pointers, which we do not know yet */ + try { - // Record the position of this command - oldOffsetToNewOffset[cmdPos.Offset] = writer.BaseStream.Position; + writer.BaseStream.Position += allScripts.Count * 0x4; + writer.Write((ushort)0xFD13); // Signal the end of header section + List useScriptCallers = new List(); - // Write the command - writer.Write((ushort)cmdPos.Command.id); - - // Write parameters, handling jumps specially - foreach (var param in cmdPos.Command.Parameters) + /* Write scripts */ + foreach (ScriptCommandContainer currentScript in allScripts) { - if (param.Type == ScriptParameter.ParameterType.RelativeJump) + if (currentScript.usedScriptID == -1) { - // For jump targets, we need to recalculate the relative offset - // based on the new positions of commands - - // Find the target offset in the original file - int targetOffset = -1; - foreach (var kvp in OffsetToLabelMap) + scriptOffsets.Add(new ContainerReference() { - if (kvp.Value == param.TargetLabel) + ID = currentScript.manualUserID, + offsetInFile = (uint)writer.BaseStream.Position + } + ); + + foreach (ScriptCommand currentCmd in currentScript.commands) + { + writer.Write((ushort)currentCmd.id); + //System.Diagnostics.Debug.Write(BitConverter.ToString(BitConverter.GetBytes(commandID)) + " "); + + List parameterList = currentCmd.cmdParams; + foreach (byte[] b in parameterList) { - targetOffset = kvp.Key; - break; + writer.Write(b); + //System.Diagnostics.Debug.WriteLine(BitConverter.ToString(parameterList[k]) + " "); } - } - if (targetOffset != -1 && oldOffsetToNewOffset.TryGetValue(targetOffset, out long newTargetOffset)) - { - // Calculate new relative offset - int relativeOffset = (int)(newTargetOffset - (writer.BaseStream.Position + 4)); - writer.Write(relativeOffset); - } - else - { - // Fallback - write original offset - writer.Write(param.TargetOffset); + /* If command calls a function/movement, store reference position */ + AddReference(ref refList, (ushort)currentCmd.id, parameterList, (int)writer.BaseStream.Position, currentScript); } } else { - // Regular parameter - writer.Write(param.RawData); + useScriptCallers.Add(currentScript); } } - } - // Update header with entry point offsets - writer.BaseStream.Position = headerStart; - foreach (var entryPoint in entryPoints) - { - if (oldOffsetToNewOffset.TryGetValue(entryPoint.Offset, out long newOffset)) + int scriptsCount = scriptOffsets.Count; + foreach (ScriptCommandContainer caller in useScriptCallers) { - uint relativeOffset = (uint)(newOffset - (headerStart + 4)); - writer.Write(relativeOffset); + for (int i = 0; i < scriptsCount; i++) + { + ContainerReference scriptReference = scriptOffsets[i]; + + if (scriptReference.ID == caller.usedScriptID) + { + scriptOffsets.Add(new ContainerReference() + { + ID = caller.manualUserID, + offsetInFile = scriptReference.offsetInFile + }); // If script has UseScript, copy offset + } + } } + + /* Write functions */ + foreach (ScriptCommandContainer currentFunction in allFunctions) + { + if (currentFunction.usedScriptID == -1) + { + functionOffsets.Add(new ContainerReference() + { + ID = currentFunction.manualUserID, + offsetInFile = (uint)writer.BaseStream.Position + } + ); + + foreach (ScriptCommand currentCmd in currentFunction.commands) + { + writer.Write((ushort)currentCmd.id); + //System.Diagnostics.Debug.Write(BitConverter.ToString(BitConverter.GetBytes(commandID)) + " "); + + List parameterList = currentCmd.cmdParams; + foreach (byte[] b in parameterList) + { + writer.Write(b); + //System.Diagnostics.Debug.Write(BitConverter.ToString(parameterList[k]) + " "); + } + + /* If command calls a function/movement, store reference position */ + + AddReference(ref refList, (ushort)currentCmd.id, parameterList, (int)writer.BaseStream.Position, currentFunction); + } + } + else + { + int functionUsescript = currentFunction.usedScriptID - 1; + if (functionUsescript >= scriptOffsets.Count) + { + MessageBox.Show($"Function #{currentFunction.manualUserID} refers to Script {currentFunction.usedScriptID}, which does not exist.\n" + + $"This Script File can't be saved.", "Can't resolve UseScript reference", MessageBoxButtons.OK, MessageBoxIcon.Error); + return null; + } + + functionOffsets.Add(new ContainerReference() + { + ID = currentFunction.manualUserID, + offsetInFile = scriptOffsets.Find(x => x.ID == currentFunction.usedScriptID).offsetInFile + }); + } + } + + // Movements must be halfword-aligned + if (writer.BaseStream.Position % 2 == 1) + { //Check if the writer's head is on an odd byte + writer.Write((byte)0x00); //Add padding + } + + /* Write movements */ + foreach (ScriptActionContainer currentAction in allActions) + { + actionOffsets.Add(new ContainerReference() + { + ID = currentAction.manualUserID, + offsetInFile = (uint)writer.BaseStream.Position + }); + + foreach (ScriptAction currentCmd in currentAction.commands) + { + writer.Write((ushort)currentCmd.id); + writer.Write((ushort)currentCmd.repetitionCount); + } + } + + /* Write script offsets to header */ + writer.BaseStream.Position = 0x0; + + scriptOffsets = scriptOffsets.OrderBy(x => x.ID).ToList(); //Write script offsets to header in the correct order + for (int i = 0; i < scriptOffsets.Count; i++) + { + writer.Write(scriptOffsets[i].offsetInFile - (uint)writer.BaseStream.Position - 0x4); + } + + SortedSet undeclaredFuncs = new SortedSet(); + SortedSet undeclaredActions = new SortedSet(); + + SortedSet uninvokedFuncs = new SortedSet(allFunctions.Select(x => x.manualUserID).ToArray()); + SortedSet unreferencedActions = new SortedSet(allActions.Select(x => x.manualUserID).ToArray()); + + //refList = refList.OrderBy(x => x.invokedID).ToList(); //Sorting is not necessary, after all... + + for (int i = 0; i < refList.Count; i++) + { + writer.BaseStream.Position = refList[i].invokedAt; //place seek head on parameter that is supposed to store the jump address + ContainerReference result; + + if (refList[i].typeOfInvoked is ContainerTypes.Action) + { //isApplyMovement + result = actionOffsets.Find(entry => entry.ID == refList[i].invokedID); + + if (result.Equals(default(ContainerReference))) + { + undeclaredActions.Add(refList[i].invokedID); + } + else + { + int relativeOffset = (int)(result.offsetInFile - refList[i].invokedAt - 4); + writer.Write(relativeOffset); + unreferencedActions.Remove(refList[i].invokedID); + } + } + else + { + result = functionOffsets.Find(entry => entry.ID == refList[i].invokedID); + + if (result.Equals(default(ContainerReference))) + { + undeclaredFuncs.Add(refList[i].invokedID); + } + else + { + int relativeOffset = (int)(result.offsetInFile - refList[i].invokedAt - 4); + writer.Write(relativeOffset); + + if (FunctionIsInvoked(refList, uninvokedFuncs, refList[i].invokedID, 0)) + { + uninvokedFuncs.Remove(refList[i].invokedID); + } + + //if (refList[i].callerType != containerTypes.Function || + // (refList[i].callerType == refList[i].invokedType && refList[i].callerID == refList[i].invokedID) || + // !uninvokedFuncs.Contains(refList[i].callerID)) { //remove reference if caller is a script, or if caller calls itself, or if caller is a function that's been invoked already + // uninvokedFuncs.Remove(refList[i].invokedID); + //} + } + } + } + + //Error check + string errorMsg = ""; + if (undeclaredFuncs.Count > 0) + { + string[] errorFunctionsUndeclared = undeclaredFuncs.ToArray().Select(x => x.ToString()).ToArray(); + errorMsg += "These Functions have been invoked but not declared: " + Environment.NewLine + string.Join(separator: ",", errorFunctionsUndeclared); + errorMsg += Environment.NewLine; + } + + if (undeclaredActions.Count > 0) + { + string[] errorActionsUndeclared = undeclaredActions.ToArray().Select(x => x.ToString()).ToArray(); + errorMsg += "These Actions have been referenced but not declared: " + Environment.NewLine + string.Join(separator: ",", errorActionsUndeclared); + errorMsg += Environment.NewLine; + } + + if (!string.IsNullOrEmpty(errorMsg)) + { + MessageBox.Show(errorMsg + Environment.NewLine + "This Script File has not been overwritten since it can not be saved.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error); + errorMsg = ""; + return null; + } + + if (uninvokedFuncs.Count > 0) + { + string[] orphanedFunctions = uninvokedFuncs.ToArray().Select(x => x.ToString()).ToArray(); + errorMsg += "Unused Function IDs detected: " + Environment.NewLine + string.Join(", ", orphanedFunctions); + errorMsg += Environment.NewLine; + errorMsg += "\nIn order for a Function to be saved, it must be invoked by a Script or by another used Function."; + errorMsg += Environment.NewLine; + errorMsg += Environment.NewLine; + } + + if (unreferencedActions.Count > 0) + { + string[] orphanedActions = unreferencedActions.ToArray().Select(x => x.ToString()).ToArray(); + errorMsg += "Unused Action IDs detected: " + Environment.NewLine + string.Join(", ", orphanedActions); + errorMsg += Environment.NewLine; + errorMsg += "\nIn order for an Action to be saved, it must be called by a Script or by a used Function."; + errorMsg += Environment.NewLine; + errorMsg += Environment.NewLine; + } + + if (!string.IsNullOrEmpty(errorMsg)) + { + MessageBox.Show(errorMsg + Environment.NewLine + "Remember that every unused Function or Action is always lost upon reloading the Script File.", "Warning!", MessageBoxButtons.OK, MessageBoxIcon.Information); + errorMsg = ""; + } + } + catch (NullReferenceException nre) + { + Console.WriteLine(nre); + return null; } } return newData.ToArray(); } + private bool FunctionIsInvoked(List refList, SortedSet uninvokedFuncsSet, uint funcID, int callCount = 0, uint? excludedCaller = null) + { + if (callCount >= 30) + { + MessageBox.Show("Something went very wrong saving this Script File!" + + "\nIt is recommended that you backup its code somewhere, to avoid losing progress.", + "Fatal error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return false; + } + + Console.WriteLine("Checking calls of function " + funcID + (excludedCaller == null ? "" : " excluding Function " + excludedCaller + " as the caller.")); + + if (!uninvokedFuncsSet.Contains(funcID)) + { + Console.WriteLine("Function " + funcID + " has already been invoked before. Nothing to check."); + return true; //Abort + } + + if (refList is null || refList.Count <= 0) + { + return false; + } + + //Find the first instance of funcID being called, excluding calls coming from an excludedCaller + //if excludedCaller is null, there's nothing to exclude: a normal search is performed. + ScriptReference sr = refList.Find(x => x.invokedID == funcID && (excludedCaller == null || x.callerID != excludedCaller)); + + if (sr is null) + { + Console.WriteLine("No reference found!!!"); + return false; + } + + if (sr.typeOfCaller is ContainerTypes.Script) + { + Console.WriteLine("Function " + funcID + " is directly called by Script " + sr.callerID); + return true; + } + + if (sr.typeOfCaller is ContainerTypes.Function) + { + if (FunctionIsInvoked(refList, uninvokedFuncsSet, sr.callerID, ++callCount, excludedCaller: sr.invokedID)) + { //check if caller function is invoked as well + Console.WriteLine("Function " + funcID + " is called by Function " + sr.callerID); + return true; + } + } + + Console.WriteLine("Function " + funcID + " is unused"); + return false; + } + public bool SaveToFileDefaultDir(int IDtoReplace, bool showSuccessMessage = true) { return SaveToFileDefaultDir(RomInfo.DirNames.scripts, IDtoReplace, showSuccessMessage); diff --git a/DS_Map/Script/ScriptReference.cs b/DS_Map/ROMFiles/ScriptReference.cs similarity index 100% rename from DS_Map/Script/ScriptReference.cs rename to DS_Map/ROMFiles/ScriptReference.cs diff --git a/DS_Map/Script/VariableValueTrigger.cs b/DS_Map/ROMFiles/VariableValueTrigger.cs similarity index 100% rename from DS_Map/Script/VariableValueTrigger.cs rename to DS_Map/ROMFiles/VariableValueTrigger.cs