diff --git a/ScriptTool/ScriptTool/Compiler.cs b/ScriptTool/ScriptTool/Compiler.cs index ec260cf..ce89f4c 100644 --- a/ScriptTool/ScriptTool/Compiler.cs +++ b/ScriptTool/ScriptTool/Compiler.cs @@ -9,10 +9,26 @@ namespace ScriptTool { public class Compiler : ICompiler { + private static int[] virtualWidths; + private static int[] renderWidths; + public IEnumerable ControlCodes { get; set; } public Dictionary AddressMap { get; set; } public Func ControlCodePredicate { get; set; } + static Compiler() + { + byte[] widths = File.ReadAllBytes("m2-widths-main.bin"); + virtualWidths = new int[widths.Length / 2]; + renderWidths = new int[widths.Length / 2]; + + for (int i = 0; i < widths.Length; i += 2) + { + virtualWidths[i / 2] = widths[i]; + renderWidths[i / 2] = widths[i + 1]; + } + } + public Compiler(IEnumerable controlCodes, Func controlCodePredicate) { @@ -81,7 +97,7 @@ namespace ScriptTool if (str[i] == '[') { if (str.IndexOf(']', i + 1) == -1) - throw new Exception("Opening bracket has no matching closing bracket"); + throw new Exception("Opening bracket has no matching closing bracket: position " + i); string[] codeStrings = str.Substring(i + 1, str.IndexOf(']', i + 1) - i - 1) .Split(' '); @@ -97,10 +113,10 @@ namespace ScriptTool if (codeString[0] == '_') { if (codeString[codeString.Length - 1] != '_') - throw new Exception("Reference has no closing underscore"); + throw new Exception("Reference has no closing underscore: position " + i); if (codeString.Length <= 2) - throw new Exception("Reference is empty"); + throw new Exception("Reference is empty: position " + i); if (!scanCodesOnly) referenceAddress += 4; @@ -123,19 +139,19 @@ namespace ScriptTool } else if (str[i] == ']') { - throw new Exception("Closing bracket has no matching opening bracket"); + throw new Exception("Closing bracket has no matching opening bracket: position " + i); } else if (str[i] == '^') { if (str.IndexOf('^', i + 1) == -1) - throw new Exception("Label has no matching closing caret"); + throw new Exception("Label has no matching closing caret: position " + i); string label = str.Substring(i + 1, str.IndexOf('^', i + 1) - i - 1); if (AddressMap.ContainsKey(label)) - throw new Exception("Label already defined"); + throw new Exception("Label already defined: position " + i); - if(!scanCodesOnly) + if (!scanCodesOnly) AddressMap.Add(label, referenceAddress); i = str.IndexOf('^', i + 1) + 1; @@ -169,7 +185,7 @@ namespace ScriptTool if (str[i] == '[') { if (str.IndexOf(']', i + 1) == -1) - throw new Exception("Opening bracket has no matching closing bracket"); + throw new Exception("Opening bracket has no matching closing bracket: position " + i); string[] codeStrings = str.Substring(i + 1, str.IndexOf(']', i + 1) - i - 1) .Split(' '); @@ -183,7 +199,7 @@ namespace ScriptTool for (int j = 0; j < codeStrings.Length; j++) { if (!IsHexByte(codeStrings[j])) - throw new Exception("Code string for unrecognized control code block must be a byte literal"); + throw new Exception("Code string for unrecognized control code block must be a byte literal: position " + i); byte value = byte.Parse(codeStrings[j], System.Globalization.NumberStyles.HexNumber); if (buffer != null) @@ -196,7 +212,7 @@ namespace ScriptTool { // Validate if (!code.IsValid(codeStrings)) - throw new Exception("Invalid control code"); + throw new Exception("Invalid control code: position " + i); // Parse code.Compile(codeStrings, buffer, ref referenceAddress, AddressMap); @@ -206,12 +222,12 @@ namespace ScriptTool } else if (str[i] == ']') { - throw new Exception("Closing bracket has no matching opening bracket"); + throw new Exception("Closing bracket has no matching opening bracket: position " + i); } else if (str[i] == '^') { if (str.IndexOf('^', i + 1) == -1) - throw new Exception("Label has no matching closing caret"); + throw new Exception("Label has no matching closing caret: position " + i); i = str.IndexOf('^', i + 1) + 1; } @@ -236,7 +252,8 @@ namespace ScriptTool int bytesWritten = buffer.Count - previousBufferSize; if (bytesWritten > padLength) - throw new Exception("Exceeded pad length"); + throw new Exception("Exceeded pad length: wrote " + bytesWritten + + " bytes, but the pad length is " + padLength + " bytes"); for (int i = bytesWritten; i < padLength; i++) { @@ -257,7 +274,7 @@ namespace ScriptTool if (str[i] == '[') { if (str.IndexOf(']', i + 1) == -1) - throw new Exception("Opening bracket has no matching closing bracket"); + throw new Exception("Opening bracket has no matching closing bracket: position " + i); sb.Append(str.Substring(i, str.IndexOf(']', i + 1) - i + 1)); @@ -265,12 +282,12 @@ namespace ScriptTool } else if (str[i] == ']') { - throw new Exception("Closing bracket has no matching opening bracket"); + throw new Exception("Closing bracket has no matching opening bracket: position " + i); } else if (str[i] == '^') { if (str.IndexOf('^', i + 1) == -1) - throw new Exception("Label has no matching closing caret"); + throw new Exception("Label has no matching closing caret: position " + i); sb.Append(str.Substring(i, str.IndexOf('^', i + 1) - i + 1)); @@ -284,5 +301,144 @@ namespace ScriptTool return sb.ToString(); } + + public IList FormatPreviewM12(string str, out IList widths, IDictionary charLookup) + { + var sb = new StringBuilder(); + widths = new List(); + int currentWidth = 0; + + var strings = new List(); + + for (int i = 0; i < str.Length; ) + { + if (str[i] == '[') + { + if (str.IndexOf(']', i + 1) == -1) + throw new Exception("Opening bracket has no matching closing bracket: position " + i); + + string[] codeStrings = str.Substring(i + 1, str.IndexOf(']', i + 1) - i - 1) + .Split(' '); + + M12ControlCode code = (M12ControlCode)ControlCodes.FirstOrDefault(c => c.IsMatch(codeStrings)); + + foreach (var codeString in codeStrings) + { + if (codeString[0] == '_') + { + if (codeString[codeString.Length - 1] != '_') + throw new Exception("Reference has no closing underscore: position " + i); + + if (codeString.Length <= 2) + throw new Exception("Reference is empty: position " + i); + } + else if (!IsHexByte(codeString)) + { + throw new Exception(String.Format( + "Encountered invalid code string at position {0}: {1}", i, codeString)); + } + } + + i = str.IndexOf(']', i + 1) + 1; + + + switch (code.Identifier) + { + case 0xC: + case 0xD: + case 0xE: + case 0xF: + case 0x10: + case 0x11: + case 0x12: + case 0x15: + case 0x1A: + case 0x2D: + case 0x9F: + case 0xAD: + // Name/item code + sb.Append("[NAME]"); + currentWidth += 60; + break; + + case 0x1: + case 0x2: + // Line break + strings.Add(sb.ToString()); + sb.Clear(); + widths.Add(currentWidth); + currentWidth = 0; + break; + + case 0x20: + sb.Append("[SMAAASH]"); + currentWidth += 72; + break; + + case 0x21: + sb.Append("[YOU WIN]"); + currentWidth += 72; + break; + + case 0x23: + case 0x63: + case 0x98: + case 0xB7: + sb.Append("[MONEY]"); + currentWidth += 36; + break; + + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + sb.Append("[STAT]"); + currentWidth += 18; + break; + + case 0x1E: + case 0x1F: + sb.Append("_"); + currentWidth += 10; + break; + } + + } + else if (str[i] == ']') + { + throw new Exception("Closing bracket has no matching opening bracket: position " + i); + } + else if (str[i] == '^') + { + if (str.IndexOf('^', i + 1) == -1) + throw new Exception("Label has no matching closing caret: position " + i); + + string label = str.Substring(i + 1, str.IndexOf('^', i + 1) - i - 1); + + i = str.IndexOf('^', i + 1) + 1; + } + else + { + if (!(str[i] == '\r') && !(str[i] == '\n')) + { + sb.Append(str[i]); + currentWidth += virtualWidths[GetByte(str[i], charLookup) - 0x50]; + } + i++; + } + } + + if (sb.Length > 0) + { + strings.Add(sb.ToString()); + widths.Add(currentWidth); + } + + return strings; + } } } diff --git a/ScriptTool/ScriptTool/ScriptTool.csproj b/ScriptTool/ScriptTool/ScriptTool.csproj index 2defe99..5259d7b 100644 --- a/ScriptTool/ScriptTool/ScriptTool.csproj +++ b/ScriptTool/ScriptTool/ScriptTool.csproj @@ -86,6 +86,9 @@ Always + + Always + diff --git a/ScriptTool/ScriptTool/m2-widths-main.bin b/ScriptTool/ScriptTool/m2-widths-main.bin new file mode 100644 index 0000000..7bdf7c4 Binary files /dev/null and b/ScriptTool/ScriptTool/m2-widths-main.bin differ diff --git a/ScriptTool/ScriptToolGui/MainForm.Designer.cs b/ScriptTool/ScriptToolGui/MainForm.Designer.cs index a7042cf..c02043c 100644 --- a/ScriptTool/ScriptToolGui/MainForm.Designer.cs +++ b/ScriptTool/ScriptToolGui/MainForm.Designer.cs @@ -33,6 +33,8 @@ this.fileMenu = new System.Windows.Forms.ToolStripMenuItem(); this.saveMenu = new System.Windows.Forms.ToolStripMenuItem(); this.topPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.label1 = new System.Windows.Forms.Label(); + this.collectionSelector = new System.Windows.Forms.ComboBox(); this.groupSelector = new System.Windows.Forms.ComboBox(); this.backButton = new System.Windows.Forms.Button(); this.mainPanel = new System.Windows.Forms.Panel(); @@ -44,6 +46,7 @@ this.m12StringEnglish = new System.Windows.Forms.TextBox(); this.lineOpsPanel = new System.Windows.Forms.FlowLayoutPanel(); this.copyCodesButton = new System.Windows.Forms.Button(); + this.previewButton = new System.Windows.Forms.Button(); this.codeSplitContainer = new System.Windows.Forms.SplitContainer(); this.codeList = new System.Windows.Forms.ListBox(); this.label2 = new System.Windows.Forms.Label(); @@ -55,9 +58,7 @@ this.statusBar = new System.Windows.Forms.StatusStrip(); this.statusLabel = new System.Windows.Forms.ToolStripStatusLabel(); this.writeTimer = new System.Windows.Forms.Timer(this.components); - this.label1 = new System.Windows.Forms.Label(); - this.collectionSelector = new System.Windows.Forms.ComboBox(); - this.previewButton = new System.Windows.Forms.Button(); + this.previewer = new ScriptToolGui.StringPreviewer(); this.mainMenu.SuspendLayout(); this.topPanel.SuspendLayout(); this.mainPanel.SuspendLayout(); @@ -67,6 +68,7 @@ this.mainSplitContainer.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.leftSplitContainer)).BeginInit(); this.leftSplitContainer.Panel1.SuspendLayout(); + this.leftSplitContainer.Panel2.SuspendLayout(); this.leftSplitContainer.SuspendLayout(); this.textBoxPanel.SuspendLayout(); this.lineOpsPanel.SuspendLayout(); @@ -118,6 +120,27 @@ this.topPanel.Size = new System.Drawing.Size(1026, 29); this.topPanel.TabIndex = 10; // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(3, 8); + this.label1.Margin = new System.Windows.Forms.Padding(3, 8, 0, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(39, 13); + this.label1.TabIndex = 13; + this.label1.Text = "Group:"; + // + // collectionSelector + // + this.collectionSelector.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.collectionSelector.FormattingEnabled = true; + this.collectionSelector.Location = new System.Drawing.Point(45, 4); + this.collectionSelector.Margin = new System.Windows.Forms.Padding(3, 4, 3, 3); + this.collectionSelector.Name = "collectionSelector"; + this.collectionSelector.Size = new System.Drawing.Size(157, 21); + this.collectionSelector.TabIndex = 14; + this.collectionSelector.SelectionChangeCommitted += new System.EventHandler(this.collectionSelector_SelectionChangeCommitted); + // // groupSelector // this.groupSelector.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; @@ -187,6 +210,10 @@ // this.leftSplitContainer.Panel1.Controls.Add(this.textBoxPanel); this.leftSplitContainer.Panel1.Controls.Add(this.lineOpsPanel); + // + // leftSplitContainer.Panel2 + // + this.leftSplitContainer.Panel2.Controls.Add(this.previewer); this.leftSplitContainer.Size = new System.Drawing.Size(800, 667); this.leftSplitContainer.SplitterDistance = 526; this.leftSplitContainer.TabIndex = 5; @@ -260,6 +287,16 @@ this.copyCodesButton.UseVisualStyleBackColor = true; this.copyCodesButton.Click += new System.EventHandler(this.copyCodesButton_Click); // + // previewButton + // + this.previewButton.Location = new System.Drawing.Point(133, 3); + this.previewButton.Name = "previewButton"; + this.previewButton.Size = new System.Drawing.Size(75, 23); + this.previewButton.TabIndex = 1; + this.previewButton.Text = "Preview"; + this.previewButton.UseVisualStyleBackColor = true; + this.previewButton.Click += new System.EventHandler(this.previewButton_Click); + // // codeSplitContainer // this.codeSplitContainer.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; @@ -384,35 +421,17 @@ this.writeTimer.Interval = 10000; this.writeTimer.Tick += new System.EventHandler(this.writeTimer_Tick); // - // label1 + // previewer // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(3, 8); - this.label1.Margin = new System.Windows.Forms.Padding(3, 8, 0, 0); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(39, 13); - this.label1.TabIndex = 13; - this.label1.Text = "Group:"; - // - // collectionSelector - // - this.collectionSelector.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.collectionSelector.FormattingEnabled = true; - this.collectionSelector.Location = new System.Drawing.Point(45, 4); - this.collectionSelector.Margin = new System.Windows.Forms.Padding(3, 4, 3, 3); - this.collectionSelector.Name = "collectionSelector"; - this.collectionSelector.Size = new System.Drawing.Size(157, 21); - this.collectionSelector.TabIndex = 14; - this.collectionSelector.SelectionChangeCommitted += new System.EventHandler(this.collectionSelector_SelectionChangeCommitted); - // - // previewButton - // - this.previewButton.Location = new System.Drawing.Point(133, 3); - this.previewButton.Name = "previewButton"; - this.previewButton.Size = new System.Drawing.Size(75, 23); - this.previewButton.TabIndex = 1; - this.previewButton.Text = "Preview"; - this.previewButton.UseVisualStyleBackColor = true; + this.previewer.AutoScroll = true; + this.previewer.CharLookup = null; + this.previewer.DisplayedString = null; + this.previewer.Dock = System.Windows.Forms.DockStyle.Fill; + this.previewer.Location = new System.Drawing.Point(0, 0); + this.previewer.M12Compiler = null; + this.previewer.Name = "previewer"; + this.previewer.Size = new System.Drawing.Size(796, 133); + this.previewer.TabIndex = 0; // // MainForm // @@ -440,6 +459,7 @@ this.mainSplitContainer.ResumeLayout(false); this.leftSplitContainer.Panel1.ResumeLayout(false); this.leftSplitContainer.Panel1.PerformLayout(); + this.leftSplitContainer.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.leftSplitContainer)).EndInit(); this.leftSplitContainer.ResumeLayout(false); this.textBoxPanel.ResumeLayout(false); @@ -491,6 +511,7 @@ private System.Windows.Forms.Label label1; private System.Windows.Forms.ComboBox collectionSelector; private System.Windows.Forms.Button previewButton; + private StringPreviewer previewer; } } diff --git a/ScriptTool/ScriptToolGui/MainForm.cs b/ScriptTool/ScriptToolGui/MainForm.cs index 19f5d4a..d115f55 100644 --- a/ScriptTool/ScriptToolGui/MainForm.cs +++ b/ScriptTool/ScriptToolGui/MainForm.cs @@ -72,6 +72,9 @@ namespace ScriptToolGui public MainForm() { InitializeComponent(); + + previewer.M12Compiler = m12Compiler; + previewer.CharLookup = ebCharLookup; ImportAllStringRefs(); ImportAllStrings(workingFolder); @@ -308,6 +311,8 @@ namespace ScriptToolGui PopulateCodeList(); PopulateReferenceList(); + + previewButton_Click(null, null); } private void SelectGroup(MatchedGroup group) @@ -388,6 +393,8 @@ namespace ScriptToolGui PopulateCodeList(); PopulateReferenceList(); + + previewButton_Click(null, null); } private void PushPreviousNavigationState() @@ -538,11 +545,22 @@ namespace ScriptToolGui if (collectionSelector.SelectedIndex == -1) { groupSelector.Items.Clear(); + previewer.DisplayedString = null; } else { var collection = (MatchedGroupCollection)collectionSelector.SelectedItem; + // Set the previewer width before we do navigation stuff + if (collection == psiHelpGroups) + { + previewer.MaxWidth = 224; + } + else + { + previewer.MaxWidth = 144; + } + // Take no action if we haven't actually changed the collection // (otherwise, the group selector would jump to 0, probably unwanted) if (collection == currentCollection) @@ -555,6 +573,11 @@ namespace ScriptToolGui groupSelector_SelectionChangeCommitted(null, null); } } + + private void previewButton_Click(object sender, EventArgs e) + { + previewer.DisplayedString = m12StringEnglish.Text; + } } enum Game diff --git a/ScriptTool/ScriptToolGui/ScriptToolGui.csproj b/ScriptTool/ScriptToolGui/ScriptToolGui.csproj index aa7dfd3..7a2ba26 100644 --- a/ScriptTool/ScriptToolGui/ScriptToolGui.csproj +++ b/ScriptTool/ScriptToolGui/ScriptToolGui.csproj @@ -81,6 +81,9 @@ Resources.resx True + + StringPreviewer.cs + SettingsSingleFileGenerator diff --git a/ScriptTool/ScriptToolGui/StringPreviewer.Designer.cs b/ScriptTool/ScriptToolGui/StringPreviewer.Designer.cs index dcf9a70..3fd68f8 100644 --- a/ScriptTool/ScriptToolGui/StringPreviewer.Designer.cs +++ b/ScriptTool/ScriptToolGui/StringPreviewer.Designer.cs @@ -28,10 +28,35 @@ /// private void InitializeComponent() { - components = new System.ComponentModel.Container(); + this.stringPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.SuspendLayout(); + // + // stringPanel + // + this.stringPanel.AutoSize = true; + this.stringPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.stringPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.stringPanel.Location = new System.Drawing.Point(0, 0); + this.stringPanel.Name = "stringPanel"; + this.stringPanel.Size = new System.Drawing.Size(0, 0); + this.stringPanel.TabIndex = 0; + // + // StringPreviewer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScroll = true; + this.Controls.Add(this.stringPanel); + this.Name = "StringPreviewer"; + this.Size = new System.Drawing.Size(324, 105); + this.ResumeLayout(false); + this.PerformLayout(); + } #endregion + + private System.Windows.Forms.FlowLayoutPanel stringPanel; + } } diff --git a/ScriptTool/ScriptToolGui/StringPreviewer.cs b/ScriptTool/ScriptToolGui/StringPreviewer.cs index aafd954..b71e8cd 100644 --- a/ScriptTool/ScriptToolGui/StringPreviewer.cs +++ b/ScriptTool/ScriptToolGui/StringPreviewer.cs @@ -7,14 +7,75 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; +using ScriptTool; namespace ScriptToolGui { public partial class StringPreviewer : UserControl { + public Compiler M12Compiler { get; set; } + public IDictionary CharLookup { get; set; } + + public int MaxWidth { get; set; } + + private string text; + public string DisplayedString + { + get + { + return text; + } + set + { + text = value; + Redraw(); + } + } + public StringPreviewer() { InitializeComponent(); } + + private void Redraw() + { + stringPanel.Controls.Clear(); + + if (M12Compiler == null || CharLookup == null || text == null) + { + return; + } + + try + { + IList widths; + IList parsed = M12Compiler.FormatPreviewM12(text, out widths, CharLookup); + + for (int i = 0; i < parsed.Count; i++) + { + var label = new Label(); + label.AutoSize = true; + label.Text = parsed[i] + "(" + widths[i] + ")"; + + if (widths[i] <= MaxWidth) + label.ForeColor = Color.Green; + else + label.ForeColor = Color.Red; + + stringPanel.Controls.Add(label); + } + } + catch (Exception e) + { + stringPanel.Controls.Clear(); + + var errLabel = new Label(); + errLabel.AutoSize = true; + errLabel.Font = new Font(errLabel.Font, FontStyle.Bold); + errLabel.Text = "Error: " + e.Message; + + stringPanel.Controls.Add(errLabel); + } + } } } diff --git a/ScriptTool/ScriptToolGui/StringPreviewer.resx b/ScriptTool/ScriptToolGui/StringPreviewer.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/ScriptTool/ScriptToolGui/StringPreviewer.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file