diff --git a/7sCarletSceneEditor/Instructions.cs b/7sCarletSceneEditor/Instructions.cs index dde2b49..b3d2ccb 100644 --- a/7sCarletSceneEditor/Instructions.cs +++ b/7sCarletSceneEditor/Instructions.cs @@ -70,7 +70,7 @@ public string Text OnContentChanged(); } } - // public override string DisplayName => base.DisplayName + " (Text)"; + public override string ContentType => "Text"; public byte[] Data { @@ -91,17 +91,19 @@ public byte[] Data public override void Write(BinaryWriter file) { byte[] data = Data; - file.Write((short)(data.Length + 4)); + file.Write((short)(data.Length + 5)); file.Write(Opcode); file.Write(data); + file.Write((byte)0); // some of them require null termination } } - public class DialogTextInstruction : TextInstruction + public class TextInstructionWithID : TextInstruction { public int ID { get; set; } - public override string Name => "DialogText"; - public DialogTextInstruction(short opcode, int id, string text) : + public override string Name => "TextInstructionWithID"; + + public TextInstructionWithID(short opcode, int id, string text) : base(opcode, text) { ID = id; @@ -110,10 +112,38 @@ public class DialogTextInstruction : TextInstruction public override void Write(BinaryWriter file) { byte[] data = Data; - file.Write((short)(data.Length + 8)); + file.Write((short)(data.Length + 9)); file.Write(Opcode); file.Write(ID); file.Write(data); + file.Write((byte)0); // some of them require null termination } } + + public class DialogTextInstruction : TextInstructionWithID + { + public override string Name => "DialogText"; + + public DialogTextInstruction(short opcode, int id, string text) : + base(opcode, id, text) + {} + } + + public class SpeakerNameInstruction : TextInstructionWithID + { + public override string Name => "SpeakerName"; + + public SpeakerNameInstruction(short opcode, int id, string text) : + base(opcode, id, text) + { } + } + + public class VoiceFileInstruction : TextInstructionWithID + { + public override string Name => "VoiceFile"; + + public VoiceFileInstruction(short opcode, int id, string text) : + base(opcode, id, text) + { } + } } diff --git a/7sCarletSceneEditor/JsonObjects.cs b/7sCarletSceneEditor/JsonObjects.cs index 0b96a19..109753c 100644 --- a/7sCarletSceneEditor/JsonObjects.cs +++ b/7sCarletSceneEditor/JsonObjects.cs @@ -10,13 +10,17 @@ namespace _7sCarletSceneEditor internal class JsonDialogInstruction { public int id { get; set; } = -1; + public string speaker { get; set; } = null; + public string voiceFile { get; set; } = null; public string[] lines { get; set; } = Array.Empty(); public JsonDialogInstruction() { } - public JsonDialogInstruction(DialogTextInstruction inst) + public JsonDialogInstruction(DialogTextInstruction inst, string speaker = null, string voice = null) { id = inst.ID; lines = inst.Text.Trim('\0').Split(new string[] { "@@" }, StringSplitOptions.None); + this.speaker = string.IsNullOrEmpty(speaker) ? null : speaker; + voiceFile = string.IsNullOrEmpty(voice) ? null : voice; } public string GetText() => string.Join("@@", lines); diff --git a/7sCarletSceneEditor/MainWindow.Designer.cs b/7sCarletSceneEditor/MainWindow.Designer.cs index 76d25b5..6c609cd 100644 --- a/7sCarletSceneEditor/MainWindow.Designer.cs +++ b/7sCarletSceneEditor/MainWindow.Designer.cs @@ -37,6 +37,8 @@ private void InitializeComponent() this.scriptsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.btnScriptExport = new System.Windows.Forms.ToolStripMenuItem(); this.btnScriptImport = new System.Windows.Forms.ToolStripMenuItem(); + this.viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.hideUnknownInstructionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.hexBox = new Be.Windows.Forms.HexBox(); this.tabView = new System.Windows.Forms.TabControl(); this.tabViewString = new System.Windows.Forms.TabPage(); @@ -46,8 +48,8 @@ private void InitializeComponent() this.columnHeaderName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.columnHeaderType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.splitContainer = new System.Windows.Forms.SplitContainer(); - this.viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.hideUnknownInstructionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.statusStrip = new System.Windows.Forms.StatusStrip(); + this.statusLabel = new System.Windows.Forms.ToolStripStatusLabel(); this.menuStrip1.SuspendLayout(); this.tabView.SuspendLayout(); this.tabViewHex.SuspendLayout(); @@ -55,6 +57,7 @@ private void InitializeComponent() this.splitContainer.Panel1.SuspendLayout(); this.splitContainer.Panel2.SuspendLayout(); this.splitContainer.SuspendLayout(); + this.statusStrip.SuspendLayout(); this.SuspendLayout(); // // menuStrip1 @@ -112,17 +115,32 @@ private void InitializeComponent() // btnScriptExport // this.btnScriptExport.Name = "btnScriptExport"; - this.btnScriptExport.Size = new System.Drawing.Size(196, 22); + this.btnScriptExport.Size = new System.Drawing.Size(119, 22); this.btnScriptExport.Text = "Export..."; this.btnScriptExport.Click += new System.EventHandler(this.btnScriptExport_Click); // // btnScriptImport // this.btnScriptImport.Name = "btnScriptImport"; - this.btnScriptImport.Size = new System.Drawing.Size(196, 22); + this.btnScriptImport.Size = new System.Drawing.Size(119, 22); this.btnScriptImport.Text = "Import..."; this.btnScriptImport.Click += new System.EventHandler(this.btnScriptImport_Click); // + // viewToolStripMenuItem + // + this.viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.hideUnknownInstructionsToolStripMenuItem}); + this.viewToolStripMenuItem.Name = "viewToolStripMenuItem"; + this.viewToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + this.viewToolStripMenuItem.Text = "View"; + // + // hideUnknownInstructionsToolStripMenuItem + // + this.hideUnknownInstructionsToolStripMenuItem.Name = "hideUnknownInstructionsToolStripMenuItem"; + this.hideUnknownInstructionsToolStripMenuItem.Size = new System.Drawing.Size(218, 22); + this.hideUnknownInstructionsToolStripMenuItem.Text = "Hide Unknown Instructions"; + this.hideUnknownInstructionsToolStripMenuItem.Click += new System.EventHandler(this.hideUnknownInstructionsToolStripMenuItem_Click); + // // hexBox // this.hexBox.ColumnInfoVisible = true; @@ -132,7 +150,7 @@ private void InitializeComponent() this.hexBox.Location = new System.Drawing.Point(3, 3); this.hexBox.Name = "hexBox"; this.hexBox.ShadowSelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(60)))), ((int)(((byte)(188)))), ((int)(((byte)(255))))); - this.hexBox.Size = new System.Drawing.Size(700, 524); + this.hexBox.Size = new System.Drawing.Size(700, 511); this.hexBox.StringViewVisible = true; this.hexBox.TabIndex = 2; this.hexBox.UseFixedBytesPerLine = true; @@ -145,7 +163,7 @@ private void InitializeComponent() this.tabView.Location = new System.Drawing.Point(0, 0); this.tabView.Name = "tabView"; this.tabView.SelectedIndex = 0; - this.tabView.Size = new System.Drawing.Size(714, 556); + this.tabView.Size = new System.Drawing.Size(714, 543); this.tabView.TabIndex = 3; // // tabViewString @@ -153,7 +171,7 @@ private void InitializeComponent() this.tabViewString.Location = new System.Drawing.Point(4, 22); this.tabViewString.Name = "tabViewString"; this.tabViewString.Padding = new System.Windows.Forms.Padding(3); - this.tabViewString.Size = new System.Drawing.Size(706, 530); + this.tabViewString.Size = new System.Drawing.Size(706, 517); this.tabViewString.TabIndex = 0; this.tabViewString.Text = "Text"; this.tabViewString.UseVisualStyleBackColor = true; @@ -164,7 +182,7 @@ private void InitializeComponent() this.tabViewHex.Location = new System.Drawing.Point(4, 22); this.tabViewHex.Name = "tabViewHex"; this.tabViewHex.Padding = new System.Windows.Forms.Padding(3); - this.tabViewHex.Size = new System.Drawing.Size(706, 530); + this.tabViewHex.Size = new System.Drawing.Size(706, 517); this.tabViewHex.TabIndex = 1; this.tabViewHex.Text = "HEX"; this.tabViewHex.UseVisualStyleBackColor = true; @@ -180,8 +198,9 @@ private void InitializeComponent() this.listView.HideSelection = false; this.listView.LabelWrap = false; this.listView.Location = new System.Drawing.Point(0, 0); + this.listView.MultiSelect = false; this.listView.Name = "listView"; - this.listView.Size = new System.Drawing.Size(254, 556); + this.listView.Size = new System.Drawing.Size(254, 543); this.listView.TabIndex = 4; this.listView.UseCompatibleStateImageBehavior = false; this.listView.View = System.Windows.Forms.View.Details; @@ -218,31 +237,32 @@ private void InitializeComponent() // this.splitContainer.Panel2.Controls.Add(this.tabView); this.splitContainer.Panel2MinSize = 100; - this.splitContainer.Size = new System.Drawing.Size(972, 556); + this.splitContainer.Size = new System.Drawing.Size(972, 543); this.splitContainer.SplitterDistance = 254; this.splitContainer.TabIndex = 5; this.splitContainer.TabStop = false; // - // viewToolStripMenuItem + // statusStrip // - this.viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.hideUnknownInstructionsToolStripMenuItem}); - this.viewToolStripMenuItem.Name = "viewToolStripMenuItem"; - this.viewToolStripMenuItem.Size = new System.Drawing.Size(44, 20); - this.viewToolStripMenuItem.Text = "View"; + this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.statusLabel}); + this.statusStrip.Location = new System.Drawing.Point(0, 573); + this.statusStrip.Name = "statusStrip"; + this.statusStrip.Size = new System.Drawing.Size(996, 22); + this.statusStrip.TabIndex = 6; + this.statusStrip.Text = "statusStrip1"; // - // hideUnknownInstructionsToolStripMenuItem + // statusLabel // - this.hideUnknownInstructionsToolStripMenuItem.Name = "hideUnknownInstructionsToolStripMenuItem"; - this.hideUnknownInstructionsToolStripMenuItem.Size = new System.Drawing.Size(218, 22); - this.hideUnknownInstructionsToolStripMenuItem.Text = "Hide Unknown Instructions"; - this.hideUnknownInstructionsToolStripMenuItem.Click += new System.EventHandler(this.hideUnknownInstructionsToolStripMenuItem_Click); + this.statusLabel.Name = "statusLabel"; + this.statusLabel.Size = new System.Drawing.Size(0, 17); // // MainWindow // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(996, 595); + this.Controls.Add(this.statusStrip); this.Controls.Add(this.splitContainer); this.Controls.Add(this.menuStrip1); this.KeyPreview = true; @@ -260,6 +280,8 @@ private void InitializeComponent() this.splitContainer.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit(); this.splitContainer.ResumeLayout(false); + this.statusStrip.ResumeLayout(false); + this.statusStrip.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -286,6 +308,8 @@ private void InitializeComponent() private System.Windows.Forms.SplitContainer splitContainer; private System.Windows.Forms.ToolStripMenuItem viewToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem hideUnknownInstructionsToolStripMenuItem; + private System.Windows.Forms.StatusStrip statusStrip; + private System.Windows.Forms.ToolStripStatusLabel statusLabel; } } diff --git a/7sCarletSceneEditor/MainWindow.cs b/7sCarletSceneEditor/MainWindow.cs index ce6b25e..12e2c07 100644 --- a/7sCarletSceneEditor/MainWindow.cs +++ b/7sCarletSceneEditor/MainWindow.cs @@ -85,12 +85,24 @@ private void ExportDialogs(string filename) if (CurrentScenario == null) throw new ArgumentNullException("No scenario is loaded!"); List objects = new List(); + string lastSpeaker = null; + string voiceFile = null; foreach (var inst in CurrentScenario) { if (inst is DialogTextInstruction d) - objects.Add(new JsonDialogInstruction(d)); + { + objects.Add(new JsonDialogInstruction(d, lastSpeaker, voiceFile)); + voiceFile = null; // reset because each text is only associated to 1 voice file + } + else if (inst is VoiceFileInstruction v) + voiceFile = v.Text; + else if (inst is SpeakerNameInstruction s) + lastSpeaker = s.Text; } - File.WriteAllText(filename, JsonConvert.SerializeObject(objects.ToArray(), Newtonsoft.Json.Formatting.Indented)); + File.WriteAllText(filename, JsonConvert.SerializeObject( + objects.ToArray(), + Newtonsoft.Json.Formatting.Indented, + new Newtonsoft.Json.JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore })); } private void ImportDialogs(string filename) @@ -238,7 +250,8 @@ private void listView_SelectedIndexChanged(object sender, EventArgs e) if ( _skipSelectionChanged || listView.SelectedIndices.Count == 0) return; - if (listView.Items[listView.SelectedIndices[0]] is InstructionListViewItem item) + int index = listView.SelectedIndices[0]; + if (listView.Items[index] is InstructionListViewItem item) { CurrentInstruction = item.Instruction; DataSource.Instruction = item.Instruction; @@ -246,10 +259,24 @@ private void listView_SelectedIndexChanged(object sender, EventArgs e) try { tabView.TabPages.Clear(); - if (item.Instruction is TextInstruction inst) + StringBuilder status = new StringBuilder(); + status.Append($"0x{item.Instruction.Opcode:X4} | {item.Instruction.Name}"); + + if (item.Instruction is ITextRepresentable instT) tabView.TabPages.Add(tabViewString); if (item.Instruction is IBinaryRepresentable instB) tabView.TabPages.Add(tabViewHex); + if (item.Instruction is DialogTextInstruction instD) + { + var str = CurrentScenario.GetSpeaker(index); + if (!string.IsNullOrEmpty(str)) + status.Append(" | Speaker: ").Append(str); + str = CurrentScenario.GetVoiceFile(index); + if (!string.IsNullOrEmpty(str)) + status.Append(" | Voice File: ").Append(str); + } + + statusLabel.Text = status.ToString(); } catch (Exception exc) { diff --git a/7sCarletSceneEditor/MainWindow.resx b/7sCarletSceneEditor/MainWindow.resx index 12b5743..89946a8 100644 --- a/7sCarletSceneEditor/MainWindow.resx +++ b/7sCarletSceneEditor/MainWindow.resx @@ -118,6 +118,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + 126, 17 + + 17, 17 diff --git a/7sCarletSceneEditor/Scenario.cs b/7sCarletSceneEditor/Scenario.cs index f305b69..b08eb5d 100644 --- a/7sCarletSceneEditor/Scenario.cs +++ b/7sCarletSceneEditor/Scenario.cs @@ -47,7 +47,19 @@ public static Scenario Load(string filename) inst = new DialogTextInstruction( opcode, BitConverter.ToInt32(data, 0), - Utility.DefaultEncoding.GetString(data, 4, data.Length - 4)); + Utility.GetStringWithoutZeros(data, 4, data.Length - 4)); + break; + case 0x0018: + inst = new SpeakerNameInstruction( + opcode, + BitConverter.ToInt32(data, 0), + Utility.GetStringWithoutZeros(data, 4, data.Length - 4)); + break; + case 0x0024: + inst = new VoiceFileInstruction( + opcode, + BitConverter.ToInt32(data, 0), + Utility.GetStringWithoutZeros(data, 4, data.Length - 4)); break; default: inst = new BinaryInstruction(opcode, data); @@ -79,5 +91,29 @@ IEnumerator IEnumerable.GetEnumerator() { return Instructions.GetEnumerator(); } + + public string GetSpeaker(int dialogInstructionIndex) + { + for (int i = dialogInstructionIndex - 1; i > 0; i--) + { + var inst = Instructions[i]; + if (inst is SpeakerNameInstruction speaker) + return speaker.Text; + } + return null; + } + + public string GetVoiceFile(int dialogInstructionIndex) + { + for (int i = dialogInstructionIndex - 1; i > 0; i--) + { + var inst = Instructions[i]; + if (inst is VoiceFileInstruction voice) + return voice.Text; + else if (inst is DialogTextInstruction dialog) + return null; // only one text per voice file + } + return null; + } } } diff --git a/7sCarletSceneEditor/Utility.cs b/7sCarletSceneEditor/Utility.cs index 9f80d24..1efa7eb 100644 --- a/7sCarletSceneEditor/Utility.cs +++ b/7sCarletSceneEditor/Utility.cs @@ -9,5 +9,15 @@ namespace _7sCarletSceneEditor public static class Utility { public static Encoding DefaultEncoding = Encoding.UTF8; + + public static string GetStringWithoutZeros(byte[] data) + { + return DefaultEncoding.GetString(data).TrimEnd('\0'); + } + + public static string GetStringWithoutZeros(byte[] data, int offset, int count) + { + return DefaultEncoding.GetString(data, offset, count).TrimEnd('\0'); + } } }