diff --git a/DarkScript3.sln b/DarkScript3.sln index dbc6374..b9ec797 100644 --- a/DarkScript3.sln +++ b/DarkScript3.sln @@ -4,9 +4,9 @@ VisualStudioVersion = 17.0.31912.275 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DarkScript3", "DarkScript3\DarkScript3.csproj", "{26132F38-F7FC-465D-8044-4D45B5F79264}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SoulsFormats", "..\SoulsFormats\SoulsFormats\SoulsFormats.csproj", "{580C3981-F179-4F17-858A-9469023BAF44}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SoulsFormats", "SoulsFormats\SoulsFormats\SoulsFormats.csproj", "{580C3981-F179-4F17-858A-9469023BAF44}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SoapstoneLib", "..\SoapstoneLib\SoapstoneLib\SoapstoneLib.csproj", "{7ADA6E29-EE8A-4829-A2D5-9C7B42D43601}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SoapstoneLib", "SoapstoneLib\SoapstoneLib\SoapstoneLib.csproj", "{7ADA6E29-EE8A-4829-A2D5-9C7B42D43601}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/DarkScript3/DarkScript3.csproj b/DarkScript3/DarkScript3.csproj index d2c9651..5e35251 100644 --- a/DarkScript3/DarkScript3.csproj +++ b/DarkScript3/DarkScript3.csproj @@ -2,7 +2,7 @@ WinExe - net6.0-windows + net8.0-windows7.0 true Resources\darkscriptico1.ico true @@ -25,8 +25,8 @@ - - + + diff --git a/DarkScript3/EditorGUI.cs b/DarkScript3/EditorGUI.cs index 2a8d92a..3d07da9 100644 --- a/DarkScript3/EditorGUI.cs +++ b/DarkScript3/EditorGUI.cs @@ -474,6 +474,11 @@ private void OnOutOfBoundsToolTip(Point p) private void Editor_ToolTipNeeded(object sender, ToolTipNeededEventArgs e) { + if (!Properties.Settings.Default.DisplayTooltips) + { + return; + } + if (PreventHoverMousePosition != null) { if (MousePosition.Equals(PreventHoverMousePosition)) @@ -530,7 +535,14 @@ void showData(SoapstoneMetadata.DisplayData data) string text = data.Desc; if ((data is SoapstoneMetadata.EntityData ent && ent.Type != "Self") || data is SoapstoneMetadata.EntryData) { - text += "\nRight-click tooltip to open in DSMapStudio"; + var name = "DSMapStudio"; + + if (DarkScript3.Properties.Settings.Default.UseSoapstoneSmithbox) + { + name = "Smithbox"; + } + + text += $"\nRight-click tooltip to open in {name}"; } ShowTip(text, p, data: data); } diff --git a/DarkScript3/GUI.Designer.cs b/DarkScript3/GUI.Designer.cs index ca27f7d..2cc0466 100644 --- a/DarkScript3/GUI.Designer.cs +++ b/DarkScript3/GUI.Designer.cs @@ -28,684 +28,652 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); + components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GUI)); - this.menuStrip = new System.Windows.Forms.MenuStrip(); - this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.openProjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.closeTabToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.batchDumpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.batchResaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.nextTabToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.previousTabToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.cutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.pasteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); - this.findToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.replaceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.findInFilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); - this.selectAllToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator(); - this.goToEventIDUnderCursorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.replaceFloatUnderCursorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripSeparator(); - this.decompileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); - this.customizeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.scriptCompilationSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.openAutoCompleteMenuToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.emevdDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.previewCompilationOutputToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.documentationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator(); - this.showArgumentsInTooltipToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.showArgumentsInPanelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.metadataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.showConnectionInfoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.clearMetadataCacheToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator6 = new System.Windows.Forms.ToolStripSeparator(); - this.connectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.viewEMEDFToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.viewEMEVDTutorialToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.viewEldenRingEMEVDTutorialToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.viewFancyDocumentationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); - this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.checkForDarkScript3UpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.statusLabel = new System.Windows.Forms.ToolStripStatusLabel(); - this.statusStrip = new System.Windows.Forms.StatusStrip(); - this.display = new System.Windows.Forms.SplitContainer(); - this.display2 = new System.Windows.Forms.SplitContainer(); - this.docBox = new FastColoredTextBoxNS.FastColoredTextBox(); - this.fileView = new System.Windows.Forms.TreeView(); - this.tabControl = new System.Windows.Forms.TabControl(); - this.clearProjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); - this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); - this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator(); - this.menuStrip.SuspendLayout(); - this.statusStrip.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.display)).BeginInit(); - this.display.Panel1.SuspendLayout(); - this.display.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.display2)).BeginInit(); - this.display2.Panel1.SuspendLayout(); - this.display2.Panel2.SuspendLayout(); - this.display2.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.docBox)).BeginInit(); - this.SuspendLayout(); + menuStrip = new System.Windows.Forms.MenuStrip(); + fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + openProjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + clearProjectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + closeTabToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); + saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + batchDumpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + batchResaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator(); + exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + nextTabToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + previousTabToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + cutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + pasteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + findToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + replaceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + findInFilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + selectAllToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator(); + goToEventIDUnderCursorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + replaceFloatUnderCursorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + optionsToolStripMenuItem = new System.Windows.Forms.ToolStripSeparator(); + decompileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + customizeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + scriptCompilationSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + openAutoCompleteMenuToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + emevdDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + previewCompilationOutputToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + documentationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator(); + showTooltipsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator(); + showArgumentsInTooltipToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + showArgumentsInPanelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + metadataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + showConnectionInfoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + clearMetadataCacheToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripSeparator6 = new System.Windows.Forms.ToolStripSeparator(); + connectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + useSmithboxForMetadataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + viewEMEDFToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + viewEMEVDTutorialToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + viewEldenRingEMEVDTutorialToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + viewFancyDocumentationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + checkForDarkScript3UpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + statusLabel = new System.Windows.Forms.ToolStripStatusLabel(); + statusStrip = new System.Windows.Forms.StatusStrip(); + display = new System.Windows.Forms.SplitContainer(); + display2 = new System.Windows.Forms.SplitContainer(); + docBox = new FastColoredTextBoxNS.FastColoredTextBox(); + fileView = new System.Windows.Forms.TreeView(); + tabControl = new System.Windows.Forms.TabControl(); + menuStrip.SuspendLayout(); + statusStrip.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)display).BeginInit(); + display.Panel1.SuspendLayout(); + display.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)display2).BeginInit(); + display2.Panel1.SuspendLayout(); + display2.Panel2.SuspendLayout(); + display2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)docBox).BeginInit(); + SuspendLayout(); // // menuStrip // - this.menuStrip.ImageScalingSize = new System.Drawing.Size(20, 20); - this.menuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.fileToolStripMenuItem, - this.editToolStripMenuItem, - this.viewToolStripMenuItem, - this.metadataToolStripMenuItem, - this.helpToolStripMenuItem}); - this.menuStrip.Location = new System.Drawing.Point(0, 0); - this.menuStrip.Name = "menuStrip"; - this.menuStrip.Size = new System.Drawing.Size(905, 24); - this.menuStrip.TabIndex = 1; - this.menuStrip.Text = "menuStrip1"; + menuStrip.ImageScalingSize = new System.Drawing.Size(20, 20); + menuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { fileToolStripMenuItem, editToolStripMenuItem, viewToolStripMenuItem, metadataToolStripMenuItem, helpToolStripMenuItem }); + menuStrip.Location = new System.Drawing.Point(0, 0); + menuStrip.Name = "menuStrip"; + menuStrip.Size = new System.Drawing.Size(905, 24); + menuStrip.TabIndex = 1; + menuStrip.Text = "menuStrip1"; // // fileToolStripMenuItem // - this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.openToolStripMenuItem, - this.openProjectToolStripMenuItem, - this.clearProjectToolStripMenuItem, - this.toolStripMenuItem2, - this.closeTabToolStripMenuItem, - this.toolStripMenuItem3, - this.saveToolStripMenuItem, - this.batchDumpToolStripMenuItem, - this.batchResaveToolStripMenuItem, - this.toolStripMenuItem4, - this.exitToolStripMenuItem, - this.nextTabToolStripMenuItem, - this.previousTabToolStripMenuItem}); - this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; - this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); - this.fileToolStripMenuItem.Text = "File"; + fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { openToolStripMenuItem, openProjectToolStripMenuItem, clearProjectToolStripMenuItem, toolStripMenuItem2, closeTabToolStripMenuItem, toolStripMenuItem3, saveToolStripMenuItem, batchDumpToolStripMenuItem, batchResaveToolStripMenuItem, toolStripMenuItem4, exitToolStripMenuItem, nextTabToolStripMenuItem, previousTabToolStripMenuItem }); + fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); + fileToolStripMenuItem.Text = "File"; // // openToolStripMenuItem // - this.openToolStripMenuItem.Name = "openToolStripMenuItem"; - this.openToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); - this.openToolStripMenuItem.Size = new System.Drawing.Size(260, 22); - this.openToolStripMenuItem.Text = "Open..."; - this.openToolStripMenuItem.Click += new System.EventHandler(this.OpenToolStripMenuItem_Click); + openToolStripMenuItem.Name = "openToolStripMenuItem"; + openToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O; + openToolStripMenuItem.Size = new System.Drawing.Size(260, 22); + openToolStripMenuItem.Text = "Open..."; + openToolStripMenuItem.Click += OpenToolStripMenuItem_Click; // // openProjectToolStripMenuItem // - this.openProjectToolStripMenuItem.Name = "openProjectToolStripMenuItem"; - this.openProjectToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) - | System.Windows.Forms.Keys.O))); - this.openProjectToolStripMenuItem.Size = new System.Drawing.Size(260, 22); - this.openProjectToolStripMenuItem.Text = "Open Project..."; - this.openProjectToolStripMenuItem.Click += new System.EventHandler(this.openProjectToolStripMenuItem_Click); + openProjectToolStripMenuItem.Name = "openProjectToolStripMenuItem"; + openProjectToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift | System.Windows.Forms.Keys.O; + openProjectToolStripMenuItem.Size = new System.Drawing.Size(260, 22); + openProjectToolStripMenuItem.Text = "Open Project..."; + openProjectToolStripMenuItem.Click += openProjectToolStripMenuItem_Click; + // + // clearProjectToolStripMenuItem + // + clearProjectToolStripMenuItem.Name = "clearProjectToolStripMenuItem"; + clearProjectToolStripMenuItem.Size = new System.Drawing.Size(260, 22); + clearProjectToolStripMenuItem.Text = "Clear Project"; + clearProjectToolStripMenuItem.Click += clearProjectToolStripMenuItem_Click; + // + // toolStripMenuItem2 + // + toolStripMenuItem2.Name = "toolStripMenuItem2"; + toolStripMenuItem2.Size = new System.Drawing.Size(257, 6); // // closeTabToolStripMenuItem // - this.closeTabToolStripMenuItem.Name = "closeTabToolStripMenuItem"; - this.closeTabToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.W))); - this.closeTabToolStripMenuItem.Size = new System.Drawing.Size(260, 22); - this.closeTabToolStripMenuItem.Text = "Close Tab"; - this.closeTabToolStripMenuItem.Click += new System.EventHandler(this.closeTabToolStripMenuItem_Click); + closeTabToolStripMenuItem.Name = "closeTabToolStripMenuItem"; + closeTabToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.W; + closeTabToolStripMenuItem.Size = new System.Drawing.Size(260, 22); + closeTabToolStripMenuItem.Text = "Close Tab"; + closeTabToolStripMenuItem.Click += closeTabToolStripMenuItem_Click; + // + // toolStripMenuItem3 + // + toolStripMenuItem3.Name = "toolStripMenuItem3"; + toolStripMenuItem3.Size = new System.Drawing.Size(257, 6); // // saveToolStripMenuItem // - this.saveToolStripMenuItem.Name = "saveToolStripMenuItem"; - this.saveToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S))); - this.saveToolStripMenuItem.Size = new System.Drawing.Size(260, 22); - this.saveToolStripMenuItem.Text = "Save"; - this.saveToolStripMenuItem.Click += new System.EventHandler(this.SaveToolStripMenuItem_Click); + saveToolStripMenuItem.Name = "saveToolStripMenuItem"; + saveToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S; + saveToolStripMenuItem.Size = new System.Drawing.Size(260, 22); + saveToolStripMenuItem.Text = "Save"; + saveToolStripMenuItem.Click += SaveToolStripMenuItem_Click; // // batchDumpToolStripMenuItem // - this.batchDumpToolStripMenuItem.Name = "batchDumpToolStripMenuItem"; - this.batchDumpToolStripMenuItem.Size = new System.Drawing.Size(260, 22); - this.batchDumpToolStripMenuItem.Text = "Batch Dump (EMEVD→JS)..."; - this.batchDumpToolStripMenuItem.Click += new System.EventHandler(this.batchDumpToolStripMenuItem_Click); + batchDumpToolStripMenuItem.Name = "batchDumpToolStripMenuItem"; + batchDumpToolStripMenuItem.Size = new System.Drawing.Size(260, 22); + batchDumpToolStripMenuItem.Text = "Batch Dump (EMEVD→JS)..."; + batchDumpToolStripMenuItem.Click += batchDumpToolStripMenuItem_Click; // // batchResaveToolStripMenuItem // - this.batchResaveToolStripMenuItem.Name = "batchResaveToolStripMenuItem"; - this.batchResaveToolStripMenuItem.Size = new System.Drawing.Size(260, 22); - this.batchResaveToolStripMenuItem.Text = "Batch Resave (JS→EMEVD)..."; - this.batchResaveToolStripMenuItem.Click += new System.EventHandler(this.batchResaveToolStripMenuItem_Click); + batchResaveToolStripMenuItem.Name = "batchResaveToolStripMenuItem"; + batchResaveToolStripMenuItem.Size = new System.Drawing.Size(260, 22); + batchResaveToolStripMenuItem.Text = "Batch Resave (JS→EMEVD)..."; + batchResaveToolStripMenuItem.Click += batchResaveToolStripMenuItem_Click; + // + // toolStripMenuItem4 + // + toolStripMenuItem4.Name = "toolStripMenuItem4"; + toolStripMenuItem4.Size = new System.Drawing.Size(257, 6); // // exitToolStripMenuItem // - this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; - this.exitToolStripMenuItem.Size = new System.Drawing.Size(260, 22); - this.exitToolStripMenuItem.Text = "Exit"; - this.exitToolStripMenuItem.Click += new System.EventHandler(this.ExitToolStripMenuItem_Click); + exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + exitToolStripMenuItem.Size = new System.Drawing.Size(260, 22); + exitToolStripMenuItem.Text = "Exit"; + exitToolStripMenuItem.Click += ExitToolStripMenuItem_Click; // // nextTabToolStripMenuItem // - this.nextTabToolStripMenuItem.Name = "nextTabToolStripMenuItem"; - this.nextTabToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Tab))); - this.nextTabToolStripMenuItem.Size = new System.Drawing.Size(260, 22); - this.nextTabToolStripMenuItem.Text = "NextTabHidden"; - this.nextTabToolStripMenuItem.Visible = false; - this.nextTabToolStripMenuItem.Click += new System.EventHandler(this.nextTabToolStripMenuItem_Click); + nextTabToolStripMenuItem.Name = "nextTabToolStripMenuItem"; + nextTabToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Tab; + nextTabToolStripMenuItem.Size = new System.Drawing.Size(260, 22); + nextTabToolStripMenuItem.Text = "NextTabHidden"; + nextTabToolStripMenuItem.Visible = false; + nextTabToolStripMenuItem.Click += nextTabToolStripMenuItem_Click; // // previousTabToolStripMenuItem // - this.previousTabToolStripMenuItem.Name = "previousTabToolStripMenuItem"; - this.previousTabToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) - | System.Windows.Forms.Keys.Tab))); - this.previousTabToolStripMenuItem.Size = new System.Drawing.Size(260, 22); - this.previousTabToolStripMenuItem.Text = "PreviousTabHidden"; - this.previousTabToolStripMenuItem.Visible = false; - this.previousTabToolStripMenuItem.Click += new System.EventHandler(this.previousTabToolStripMenuItem_Click); + previousTabToolStripMenuItem.Name = "previousTabToolStripMenuItem"; + previousTabToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift | System.Windows.Forms.Keys.Tab; + previousTabToolStripMenuItem.Size = new System.Drawing.Size(260, 22); + previousTabToolStripMenuItem.Text = "PreviousTabHidden"; + previousTabToolStripMenuItem.Visible = false; + previousTabToolStripMenuItem.Click += previousTabToolStripMenuItem_Click; // // editToolStripMenuItem // - this.editToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.cutToolStripMenuItem, - this.copyToolStripMenuItem, - this.pasteToolStripMenuItem, - this.toolStripSeparator1, - this.findToolStripMenuItem, - this.replaceToolStripMenuItem, - this.findInFilesToolStripMenuItem, - this.toolStripSeparator2, - this.selectAllToolStripMenuItem, - this.toolStripSeparator4, - this.goToEventIDUnderCursorToolStripMenuItem, - this.replaceFloatUnderCursorToolStripMenuItem, - this.optionsToolStripMenuItem, - this.decompileToolStripMenuItem, - this.toolStripSeparator3, - this.customizeToolStripMenuItem, - this.scriptCompilationSettingsToolStripMenuItem, - this.openAutoCompleteMenuToolStripMenuItem}); - this.editToolStripMenuItem.Name = "editToolStripMenuItem"; - this.editToolStripMenuItem.Size = new System.Drawing.Size(39, 20); - this.editToolStripMenuItem.Text = "Edit"; + editToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { cutToolStripMenuItem, copyToolStripMenuItem, pasteToolStripMenuItem, toolStripSeparator1, findToolStripMenuItem, replaceToolStripMenuItem, findInFilesToolStripMenuItem, toolStripSeparator2, selectAllToolStripMenuItem, toolStripSeparator4, goToEventIDUnderCursorToolStripMenuItem, replaceFloatUnderCursorToolStripMenuItem, optionsToolStripMenuItem, decompileToolStripMenuItem, toolStripSeparator3, customizeToolStripMenuItem, scriptCompilationSettingsToolStripMenuItem, openAutoCompleteMenuToolStripMenuItem }); + editToolStripMenuItem.Name = "editToolStripMenuItem"; + editToolStripMenuItem.Size = new System.Drawing.Size(39, 20); + editToolStripMenuItem.Text = "Edit"; // // cutToolStripMenuItem // - this.cutToolStripMenuItem.Name = "cutToolStripMenuItem"; - this.cutToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+X"; - this.cutToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.cutToolStripMenuItem.Text = "Cut"; - this.cutToolStripMenuItem.Click += new System.EventHandler(this.CutToolStripMenuItem_Click); + cutToolStripMenuItem.Name = "cutToolStripMenuItem"; + cutToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+X"; + cutToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + cutToolStripMenuItem.Text = "Cut"; + cutToolStripMenuItem.Click += CutToolStripMenuItem_Click; // // copyToolStripMenuItem // - this.copyToolStripMenuItem.Name = "copyToolStripMenuItem"; - this.copyToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+C"; - this.copyToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.copyToolStripMenuItem.Text = "Copy"; - this.copyToolStripMenuItem.Click += new System.EventHandler(this.CopyToolStripMenuItem_Click); + copyToolStripMenuItem.Name = "copyToolStripMenuItem"; + copyToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+C"; + copyToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + copyToolStripMenuItem.Text = "Copy"; + copyToolStripMenuItem.Click += CopyToolStripMenuItem_Click; // // pasteToolStripMenuItem // - this.pasteToolStripMenuItem.Name = "pasteToolStripMenuItem"; - this.pasteToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+V"; - this.pasteToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.pasteToolStripMenuItem.Text = "Paste"; - this.pasteToolStripMenuItem.Click += new System.EventHandler(this.pasteToolStripMenuItem_Click); + pasteToolStripMenuItem.Name = "pasteToolStripMenuItem"; + pasteToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+V"; + pasteToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + pasteToolStripMenuItem.Text = "Paste"; + pasteToolStripMenuItem.Click += pasteToolStripMenuItem_Click; // // toolStripSeparator1 // - this.toolStripSeparator1.Name = "toolStripSeparator1"; - this.toolStripSeparator1.Size = new System.Drawing.Size(274, 6); + toolStripSeparator1.Name = "toolStripSeparator1"; + toolStripSeparator1.Size = new System.Drawing.Size(274, 6); // // findToolStripMenuItem // - this.findToolStripMenuItem.Name = "findToolStripMenuItem"; - this.findToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+F"; - this.findToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.findToolStripMenuItem.Text = "Find"; - this.findToolStripMenuItem.Click += new System.EventHandler(this.FindToolStripMenuItem_Click); + findToolStripMenuItem.Name = "findToolStripMenuItem"; + findToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+F"; + findToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + findToolStripMenuItem.Text = "Find"; + findToolStripMenuItem.Click += FindToolStripMenuItem_Click; // // replaceToolStripMenuItem // - this.replaceToolStripMenuItem.Name = "replaceToolStripMenuItem"; - this.replaceToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+H"; - this.replaceToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.replaceToolStripMenuItem.Text = "Replace"; - this.replaceToolStripMenuItem.Click += new System.EventHandler(this.ReplaceToolStripMenuItem_Click); + replaceToolStripMenuItem.Name = "replaceToolStripMenuItem"; + replaceToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+H"; + replaceToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + replaceToolStripMenuItem.Text = "Replace"; + replaceToolStripMenuItem.Click += ReplaceToolStripMenuItem_Click; // // findInFilesToolStripMenuItem // - this.findInFilesToolStripMenuItem.Name = "findInFilesToolStripMenuItem"; - this.findInFilesToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) - | System.Windows.Forms.Keys.F))); - this.findInFilesToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.findInFilesToolStripMenuItem.Text = "Find in Files..."; - this.findInFilesToolStripMenuItem.Click += new System.EventHandler(this.findInFilesToolStripMenuItem_Click); + findInFilesToolStripMenuItem.Name = "findInFilesToolStripMenuItem"; + findInFilesToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift | System.Windows.Forms.Keys.F; + findInFilesToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + findInFilesToolStripMenuItem.Text = "Find in Files..."; + findInFilesToolStripMenuItem.Click += findInFilesToolStripMenuItem_Click; // // toolStripSeparator2 // - this.toolStripSeparator2.Name = "toolStripSeparator2"; - this.toolStripSeparator2.Size = new System.Drawing.Size(274, 6); + toolStripSeparator2.Name = "toolStripSeparator2"; + toolStripSeparator2.Size = new System.Drawing.Size(274, 6); // // selectAllToolStripMenuItem // - this.selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem"; - this.selectAllToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+A"; - this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.selectAllToolStripMenuItem.Text = "Select All"; - this.selectAllToolStripMenuItem.Click += new System.EventHandler(this.SelectAllToolStripMenuItem_Click); + selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem"; + selectAllToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+A"; + selectAllToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + selectAllToolStripMenuItem.Text = "Select All"; + selectAllToolStripMenuItem.Click += SelectAllToolStripMenuItem_Click; // // toolStripSeparator4 // - this.toolStripSeparator4.Name = "toolStripSeparator4"; - this.toolStripSeparator4.Size = new System.Drawing.Size(274, 6); + toolStripSeparator4.Name = "toolStripSeparator4"; + toolStripSeparator4.Size = new System.Drawing.Size(274, 6); // // goToEventIDUnderCursorToolStripMenuItem // - this.goToEventIDUnderCursorToolStripMenuItem.Name = "goToEventIDUnderCursorToolStripMenuItem"; - this.goToEventIDUnderCursorToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+Enter"; - this.goToEventIDUnderCursorToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.goToEventIDUnderCursorToolStripMenuItem.Text = "Go to Event ID"; - this.goToEventIDUnderCursorToolStripMenuItem.Click += new System.EventHandler(this.goToEventIDUnderCursorToolStripMenuItem_Click); + goToEventIDUnderCursorToolStripMenuItem.Name = "goToEventIDUnderCursorToolStripMenuItem"; + goToEventIDUnderCursorToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+Enter"; + goToEventIDUnderCursorToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + goToEventIDUnderCursorToolStripMenuItem.Text = "Go to Event ID"; + goToEventIDUnderCursorToolStripMenuItem.Click += goToEventIDUnderCursorToolStripMenuItem_Click; // // replaceFloatUnderCursorToolStripMenuItem // - this.replaceFloatUnderCursorToolStripMenuItem.Name = "replaceFloatUnderCursorToolStripMenuItem"; - this.replaceFloatUnderCursorToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+1"; - this.replaceFloatUnderCursorToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.replaceFloatUnderCursorToolStripMenuItem.Text = "Replace Float"; - this.replaceFloatUnderCursorToolStripMenuItem.Click += new System.EventHandler(this.replaceFloatUnderCursorToolStripMenuItem_Click); + replaceFloatUnderCursorToolStripMenuItem.Name = "replaceFloatUnderCursorToolStripMenuItem"; + replaceFloatUnderCursorToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+1"; + replaceFloatUnderCursorToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + replaceFloatUnderCursorToolStripMenuItem.Text = "Replace Float"; + replaceFloatUnderCursorToolStripMenuItem.Click += replaceFloatUnderCursorToolStripMenuItem_Click; // // optionsToolStripMenuItem // - this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; - this.optionsToolStripMenuItem.Size = new System.Drawing.Size(274, 6); + optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; + optionsToolStripMenuItem.Size = new System.Drawing.Size(274, 6); // // decompileToolStripMenuItem // - this.decompileToolStripMenuItem.Name = "decompileToolStripMenuItem"; - this.decompileToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.decompileToolStripMenuItem.Text = "Preview Conversion to MattScript..."; - this.decompileToolStripMenuItem.Click += new System.EventHandler(this.decompileToolStripMenuItem_Click); + decompileToolStripMenuItem.Name = "decompileToolStripMenuItem"; + decompileToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + decompileToolStripMenuItem.Text = "Preview Conversion to MattScript..."; + decompileToolStripMenuItem.Click += decompileToolStripMenuItem_Click; // // toolStripSeparator3 // - this.toolStripSeparator3.Name = "toolStripSeparator3"; - this.toolStripSeparator3.Size = new System.Drawing.Size(274, 6); + toolStripSeparator3.Name = "toolStripSeparator3"; + toolStripSeparator3.Size = new System.Drawing.Size(274, 6); // // customizeToolStripMenuItem // - this.customizeToolStripMenuItem.Name = "customizeToolStripMenuItem"; - this.customizeToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.customizeToolStripMenuItem.Text = "Customize Appearance..."; - this.customizeToolStripMenuItem.Click += new System.EventHandler(this.customizeToolStripMenuItem_Click); + customizeToolStripMenuItem.Name = "customizeToolStripMenuItem"; + customizeToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + customizeToolStripMenuItem.Text = "Customize Appearance..."; + customizeToolStripMenuItem.Click += customizeToolStripMenuItem_Click; // // scriptCompilationSettingsToolStripMenuItem // - this.scriptCompilationSettingsToolStripMenuItem.Name = "scriptCompilationSettingsToolStripMenuItem"; - this.scriptCompilationSettingsToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.scriptCompilationSettingsToolStripMenuItem.Text = "Script Compilation Settings..."; - this.scriptCompilationSettingsToolStripMenuItem.Click += new System.EventHandler(this.scriptCompilationSettingsToolStripMenuItem_Click); + scriptCompilationSettingsToolStripMenuItem.Name = "scriptCompilationSettingsToolStripMenuItem"; + scriptCompilationSettingsToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + scriptCompilationSettingsToolStripMenuItem.Text = "Script Compilation Settings..."; + scriptCompilationSettingsToolStripMenuItem.Click += scriptCompilationSettingsToolStripMenuItem_Click; // // openAutoCompleteMenuToolStripMenuItem // - this.openAutoCompleteMenuToolStripMenuItem.Name = "openAutoCompleteMenuToolStripMenuItem"; - this.openAutoCompleteMenuToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Space))); - this.openAutoCompleteMenuToolStripMenuItem.Size = new System.Drawing.Size(277, 22); - this.openAutoCompleteMenuToolStripMenuItem.Text = "OpenAutoCompleteMenu"; - this.openAutoCompleteMenuToolStripMenuItem.Visible = false; - this.openAutoCompleteMenuToolStripMenuItem.Click += new System.EventHandler(this.openAutoCompleteMenuToolStripMenuItem_Click); + openAutoCompleteMenuToolStripMenuItem.Name = "openAutoCompleteMenuToolStripMenuItem"; + openAutoCompleteMenuToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Space; + openAutoCompleteMenuToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + openAutoCompleteMenuToolStripMenuItem.Text = "OpenAutoCompleteMenu"; + openAutoCompleteMenuToolStripMenuItem.Visible = false; + openAutoCompleteMenuToolStripMenuItem.Click += openAutoCompleteMenuToolStripMenuItem_Click; // // viewToolStripMenuItem // - this.viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.emevdDataToolStripMenuItem, - this.previewCompilationOutputToolStripMenuItem, - this.documentationToolStripMenuItem, - this.toolStripSeparator5, - this.showArgumentsInTooltipToolStripMenuItem, - this.showArgumentsInPanelToolStripMenuItem}); - this.viewToolStripMenuItem.Name = "viewToolStripMenuItem"; - this.viewToolStripMenuItem.Size = new System.Drawing.Size(44, 20); - this.viewToolStripMenuItem.Text = "View"; + viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { emevdDataToolStripMenuItem, previewCompilationOutputToolStripMenuItem, documentationToolStripMenuItem, toolStripSeparator7, showTooltipsToolStripMenuItem, toolStripSeparator5, showArgumentsInTooltipToolStripMenuItem, showArgumentsInPanelToolStripMenuItem }); + viewToolStripMenuItem.Name = "viewToolStripMenuItem"; + viewToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + viewToolStripMenuItem.Text = "View"; // // emevdDataToolStripMenuItem // - this.emevdDataToolStripMenuItem.Name = "emevdDataToolStripMenuItem"; - this.emevdDataToolStripMenuItem.Size = new System.Drawing.Size(234, 22); - this.emevdDataToolStripMenuItem.Text = "EMEVD Data..."; - this.emevdDataToolStripMenuItem.Click += new System.EventHandler(this.EmevdDataToolStripMenuItem_Click); + emevdDataToolStripMenuItem.Name = "emevdDataToolStripMenuItem"; + emevdDataToolStripMenuItem.Size = new System.Drawing.Size(234, 22); + emevdDataToolStripMenuItem.Text = "EMEVD Data..."; + emevdDataToolStripMenuItem.Click += EmevdDataToolStripMenuItem_Click; // // previewCompilationOutputToolStripMenuItem // - this.previewCompilationOutputToolStripMenuItem.Name = "previewCompilationOutputToolStripMenuItem"; - this.previewCompilationOutputToolStripMenuItem.Size = new System.Drawing.Size(234, 22); - this.previewCompilationOutputToolStripMenuItem.Text = "Preview Compilation Output..."; - this.previewCompilationOutputToolStripMenuItem.Click += new System.EventHandler(this.previewCompilationOutputToolStripMenuItem_Click); + previewCompilationOutputToolStripMenuItem.Name = "previewCompilationOutputToolStripMenuItem"; + previewCompilationOutputToolStripMenuItem.Size = new System.Drawing.Size(234, 22); + previewCompilationOutputToolStripMenuItem.Text = "Preview Compilation Output..."; + previewCompilationOutputToolStripMenuItem.Click += previewCompilationOutputToolStripMenuItem_Click; // // documentationToolStripMenuItem // - this.documentationToolStripMenuItem.Name = "documentationToolStripMenuItem"; - this.documentationToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) - | System.Windows.Forms.Keys.D))); - this.documentationToolStripMenuItem.Size = new System.Drawing.Size(234, 22); - this.documentationToolStripMenuItem.Text = "Toggle Panel"; - this.documentationToolStripMenuItem.Click += new System.EventHandler(this.DocumentationToolStripMenuItem_Click); + documentationToolStripMenuItem.Name = "documentationToolStripMenuItem"; + documentationToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift | System.Windows.Forms.Keys.D; + documentationToolStripMenuItem.Size = new System.Drawing.Size(234, 22); + documentationToolStripMenuItem.Text = "Toggle Panel"; + documentationToolStripMenuItem.Click += DocumentationToolStripMenuItem_Click; + // + // toolStripSeparator7 + // + toolStripSeparator7.Name = "toolStripSeparator7"; + toolStripSeparator7.Size = new System.Drawing.Size(231, 6); + // + // showTooltipsToolStripMenuItem + // + showTooltipsToolStripMenuItem.CheckOnClick = true; + showTooltipsToolStripMenuItem.Name = "showTooltipsToolStripMenuItem"; + showTooltipsToolStripMenuItem.Size = new System.Drawing.Size(234, 22); + showTooltipsToolStripMenuItem.Text = "Display Tooltips"; + showTooltipsToolStripMenuItem.CheckedChanged += showTooltipsToolStripMenuItem_CheckedChanged; // // toolStripSeparator5 // - this.toolStripSeparator5.Name = "toolStripSeparator5"; - this.toolStripSeparator5.Size = new System.Drawing.Size(231, 6); + toolStripSeparator5.Name = "toolStripSeparator5"; + toolStripSeparator5.Size = new System.Drawing.Size(231, 6); // // showArgumentsInTooltipToolStripMenuItem // - this.showArgumentsInTooltipToolStripMenuItem.CheckOnClick = true; - this.showArgumentsInTooltipToolStripMenuItem.Name = "showArgumentsInTooltipToolStripMenuItem"; - this.showArgumentsInTooltipToolStripMenuItem.Size = new System.Drawing.Size(234, 22); - this.showArgumentsInTooltipToolStripMenuItem.Text = "Show Arguments in Tooltip"; - this.showArgumentsInTooltipToolStripMenuItem.CheckedChanged += new System.EventHandler(this.showArgumentTooltipsToolStripMenuItem_CheckedChanged); + showArgumentsInTooltipToolStripMenuItem.CheckOnClick = true; + showArgumentsInTooltipToolStripMenuItem.Name = "showArgumentsInTooltipToolStripMenuItem"; + showArgumentsInTooltipToolStripMenuItem.Size = new System.Drawing.Size(234, 22); + showArgumentsInTooltipToolStripMenuItem.Text = "Show Arguments in Tooltip"; + showArgumentsInTooltipToolStripMenuItem.CheckedChanged += showArgumentTooltipsToolStripMenuItem_CheckedChanged; // // showArgumentsInPanelToolStripMenuItem // - this.showArgumentsInPanelToolStripMenuItem.CheckOnClick = true; - this.showArgumentsInPanelToolStripMenuItem.Name = "showArgumentsInPanelToolStripMenuItem"; - this.showArgumentsInPanelToolStripMenuItem.Size = new System.Drawing.Size(234, 22); - this.showArgumentsInPanelToolStripMenuItem.Text = "Show Arguments in Panel"; - this.showArgumentsInPanelToolStripMenuItem.CheckedChanged += new System.EventHandler(this.showArgumentsInPanelToolStripMenuItem_CheckedChanged); + showArgumentsInPanelToolStripMenuItem.CheckOnClick = true; + showArgumentsInPanelToolStripMenuItem.Name = "showArgumentsInPanelToolStripMenuItem"; + showArgumentsInPanelToolStripMenuItem.Size = new System.Drawing.Size(234, 22); + showArgumentsInPanelToolStripMenuItem.Text = "Show Arguments in Panel"; + showArgumentsInPanelToolStripMenuItem.CheckedChanged += showArgumentsInPanelToolStripMenuItem_CheckedChanged; // // metadataToolStripMenuItem // - this.metadataToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.showConnectionInfoToolStripMenuItem, - this.clearMetadataCacheToolStripMenuItem, - this.toolStripSeparator6, - this.connectToolStripMenuItem}); - this.metadataToolStripMenuItem.Name = "metadataToolStripMenuItem"; - this.metadataToolStripMenuItem.Size = new System.Drawing.Size(69, 20); - this.metadataToolStripMenuItem.Text = "Metadata"; + metadataToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { showConnectionInfoToolStripMenuItem, clearMetadataCacheToolStripMenuItem, toolStripSeparator6, connectToolStripMenuItem, useSmithboxForMetadataToolStripMenuItem }); + metadataToolStripMenuItem.Name = "metadataToolStripMenuItem"; + metadataToolStripMenuItem.Size = new System.Drawing.Size(69, 20); + metadataToolStripMenuItem.Text = "Metadata"; // // showConnectionInfoToolStripMenuItem // - this.showConnectionInfoToolStripMenuItem.Name = "showConnectionInfoToolStripMenuItem"; - this.showConnectionInfoToolStripMenuItem.Size = new System.Drawing.Size(276, 22); - this.showConnectionInfoToolStripMenuItem.Text = "Show DSMapStudio Connection Info..."; - this.showConnectionInfoToolStripMenuItem.Click += new System.EventHandler(this.showConnectionInfoToolStripMenuItem_Click); + showConnectionInfoToolStripMenuItem.Name = "showConnectionInfoToolStripMenuItem"; + showConnectionInfoToolStripMenuItem.Size = new System.Drawing.Size(239, 22); + showConnectionInfoToolStripMenuItem.Text = "Show Connection Info..."; + showConnectionInfoToolStripMenuItem.Click += showConnectionInfoToolStripMenuItem_Click; // // clearMetadataCacheToolStripMenuItem // - this.clearMetadataCacheToolStripMenuItem.Name = "clearMetadataCacheToolStripMenuItem"; - this.clearMetadataCacheToolStripMenuItem.Size = new System.Drawing.Size(276, 22); - this.clearMetadataCacheToolStripMenuItem.Text = "Clear Metadata Cache"; - this.clearMetadataCacheToolStripMenuItem.Click += new System.EventHandler(this.clearMetadataCacheToolStripMenuItem_Click); + clearMetadataCacheToolStripMenuItem.Name = "clearMetadataCacheToolStripMenuItem"; + clearMetadataCacheToolStripMenuItem.Size = new System.Drawing.Size(239, 22); + clearMetadataCacheToolStripMenuItem.Text = "Clear Metadata Cache"; + clearMetadataCacheToolStripMenuItem.Click += clearMetadataCacheToolStripMenuItem_Click; // // toolStripSeparator6 // - this.toolStripSeparator6.Name = "toolStripSeparator6"; - this.toolStripSeparator6.Size = new System.Drawing.Size(273, 6); + toolStripSeparator6.Name = "toolStripSeparator6"; + toolStripSeparator6.Size = new System.Drawing.Size(236, 6); // // connectToolStripMenuItem // - this.connectToolStripMenuItem.CheckOnClick = true; - this.connectToolStripMenuItem.Name = "connectToolStripMenuItem"; - this.connectToolStripMenuItem.Size = new System.Drawing.Size(276, 22); - this.connectToolStripMenuItem.Text = "Use DSMapStudio for Metadata"; - this.connectToolStripMenuItem.CheckedChanged += new System.EventHandler(this.connectToolStripMenuItem_CheckedChanged); + connectToolStripMenuItem.CheckOnClick = true; + connectToolStripMenuItem.Name = "connectToolStripMenuItem"; + connectToolStripMenuItem.Size = new System.Drawing.Size(239, 22); + connectToolStripMenuItem.Text = "Use DSMapStudio for Metadata"; + connectToolStripMenuItem.CheckedChanged += connectToolStripMenuItem_CheckedChanged; + // + // useSmithboxForMetadataToolStripMenuItem + // + useSmithboxForMetadataToolStripMenuItem.CheckOnClick = true; + useSmithboxForMetadataToolStripMenuItem.Name = "useSmithboxForMetadataToolStripMenuItem"; + useSmithboxForMetadataToolStripMenuItem.Size = new System.Drawing.Size(239, 22); + useSmithboxForMetadataToolStripMenuItem.Text = "Use Smithbox for Metadata"; + useSmithboxForMetadataToolStripMenuItem.CheckedChanged += useSmithboxForMetadataToolStripMenuItem_CheckedChanged; // // helpToolStripMenuItem // - this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.viewEMEDFToolStripMenuItem, - this.viewEMEVDTutorialToolStripMenuItem, - this.viewEldenRingEMEVDTutorialToolStripMenuItem, - this.viewFancyDocumentationToolStripMenuItem, - this.toolStripMenuItem1, - this.aboutToolStripMenuItem, - this.checkForDarkScript3UpdatesToolStripMenuItem}); - this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; - this.helpToolStripMenuItem.Size = new System.Drawing.Size(44, 20); - this.helpToolStripMenuItem.Text = "Help"; + helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { viewEMEDFToolStripMenuItem, viewEMEVDTutorialToolStripMenuItem, viewEldenRingEMEVDTutorialToolStripMenuItem, viewFancyDocumentationToolStripMenuItem, toolStripMenuItem1, aboutToolStripMenuItem, checkForDarkScript3UpdatesToolStripMenuItem }); + helpToolStripMenuItem.Name = "helpToolStripMenuItem"; + helpToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + helpToolStripMenuItem.Text = "Help"; // // viewEMEDFToolStripMenuItem // - this.viewEMEDFToolStripMenuItem.Name = "viewEMEDFToolStripMenuItem"; - this.viewEMEDFToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.viewEMEDFToolStripMenuItem.Text = "View EMEDF (List of Instructions)"; - this.viewEMEDFToolStripMenuItem.Click += new System.EventHandler(this.viewEMEDFToolStripMenuItem_Click); + viewEMEDFToolStripMenuItem.Name = "viewEMEDFToolStripMenuItem"; + viewEMEDFToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + viewEMEDFToolStripMenuItem.Text = "View EMEDF (List of Instructions)"; + viewEMEDFToolStripMenuItem.Click += viewEMEDFToolStripMenuItem_Click; // // viewEMEVDTutorialToolStripMenuItem // - this.viewEMEVDTutorialToolStripMenuItem.Name = "viewEMEVDTutorialToolStripMenuItem"; - this.viewEMEVDTutorialToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.viewEMEVDTutorialToolStripMenuItem.Text = "View EMEVD Tutorial"; - this.viewEMEVDTutorialToolStripMenuItem.Click += new System.EventHandler(this.viewEMEVDTutorialToolStripMenuItem_Click); + viewEMEVDTutorialToolStripMenuItem.Name = "viewEMEVDTutorialToolStripMenuItem"; + viewEMEVDTutorialToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + viewEMEVDTutorialToolStripMenuItem.Text = "View EMEVD Tutorial"; + viewEMEVDTutorialToolStripMenuItem.Click += viewEMEVDTutorialToolStripMenuItem_Click; // // viewEldenRingEMEVDTutorialToolStripMenuItem // - this.viewEldenRingEMEVDTutorialToolStripMenuItem.Name = "viewEldenRingEMEVDTutorialToolStripMenuItem"; - this.viewEldenRingEMEVDTutorialToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.viewEldenRingEMEVDTutorialToolStripMenuItem.Text = "View Elden Ring EMEVD Tutorial"; - this.viewEldenRingEMEVDTutorialToolStripMenuItem.Click += new System.EventHandler(this.viewEldenRingEMEVDTutorialToolStripMenuItem_Click); + viewEldenRingEMEVDTutorialToolStripMenuItem.Name = "viewEldenRingEMEVDTutorialToolStripMenuItem"; + viewEldenRingEMEVDTutorialToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + viewEldenRingEMEVDTutorialToolStripMenuItem.Text = "View Elden Ring EMEVD Tutorial"; + viewEldenRingEMEVDTutorialToolStripMenuItem.Click += viewEldenRingEMEVDTutorialToolStripMenuItem_Click; // // viewFancyDocumentationToolStripMenuItem // - this.viewFancyDocumentationToolStripMenuItem.Name = "viewFancyDocumentationToolStripMenuItem"; - this.viewFancyDocumentationToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.viewFancyDocumentationToolStripMenuItem.Text = "View MattScript Documentation"; - this.viewFancyDocumentationToolStripMenuItem.Click += new System.EventHandler(this.viewFancyDocumentationToolStripMenuItem_Click); + viewFancyDocumentationToolStripMenuItem.Name = "viewFancyDocumentationToolStripMenuItem"; + viewFancyDocumentationToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + viewFancyDocumentationToolStripMenuItem.Text = "View MattScript Documentation"; + viewFancyDocumentationToolStripMenuItem.Click += viewFancyDocumentationToolStripMenuItem_Click; // // toolStripMenuItem1 // - this.toolStripMenuItem1.Name = "toolStripMenuItem1"; - this.toolStripMenuItem1.Size = new System.Drawing.Size(244, 6); + toolStripMenuItem1.Name = "toolStripMenuItem1"; + toolStripMenuItem1.Size = new System.Drawing.Size(244, 6); // // aboutToolStripMenuItem // - this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; - this.aboutToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.aboutToolStripMenuItem.Text = "About DarkScript3"; - this.aboutToolStripMenuItem.Click += new System.EventHandler(this.AboutToolStripMenuItem_Click); + aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; + aboutToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + aboutToolStripMenuItem.Text = "About DarkScript3"; + aboutToolStripMenuItem.Click += AboutToolStripMenuItem_Click; // // checkForDarkScript3UpdatesToolStripMenuItem // - this.checkForDarkScript3UpdatesToolStripMenuItem.Name = "checkForDarkScript3UpdatesToolStripMenuItem"; - this.checkForDarkScript3UpdatesToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.checkForDarkScript3UpdatesToolStripMenuItem.Text = "Check for DarkScript3 Updates"; - this.checkForDarkScript3UpdatesToolStripMenuItem.Click += new System.EventHandler(this.checkForDarkScript3UpdatesToolStripMenuItem_Click); + checkForDarkScript3UpdatesToolStripMenuItem.Name = "checkForDarkScript3UpdatesToolStripMenuItem"; + checkForDarkScript3UpdatesToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + checkForDarkScript3UpdatesToolStripMenuItem.Text = "Check for DarkScript3 Updates"; + checkForDarkScript3UpdatesToolStripMenuItem.Click += checkForDarkScript3UpdatesToolStripMenuItem_Click; // // statusLabel // - this.statusLabel.Name = "statusLabel"; - this.statusLabel.Size = new System.Drawing.Size(66, 17); - this.statusLabel.Text = "statusLabel"; + statusLabel.Name = "statusLabel"; + statusLabel.Size = new System.Drawing.Size(66, 17); + statusLabel.Text = "statusLabel"; // // statusStrip // - this.statusStrip.AutoSize = false; - this.statusStrip.ImageScalingSize = new System.Drawing.Size(20, 20); - this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.statusLabel}); - this.statusStrip.Location = new System.Drawing.Point(0, 535); - this.statusStrip.Name = "statusStrip"; - this.statusStrip.Size = new System.Drawing.Size(905, 22); - this.statusStrip.SizingGrip = false; - this.statusStrip.TabIndex = 3; - this.statusStrip.Text = "statusStrip1"; + statusStrip.AutoSize = false; + statusStrip.ImageScalingSize = new System.Drawing.Size(20, 20); + statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { statusLabel }); + statusStrip.Location = new System.Drawing.Point(0, 535); + statusStrip.Name = "statusStrip"; + statusStrip.Size = new System.Drawing.Size(905, 22); + statusStrip.SizingGrip = false; + statusStrip.TabIndex = 3; + statusStrip.Text = "statusStrip1"; // // display // - this.display.Dock = System.Windows.Forms.DockStyle.Fill; - this.display.IsSplitterFixed = true; - this.display.Location = new System.Drawing.Point(0, 47); - this.display.Name = "display"; + display.Dock = System.Windows.Forms.DockStyle.Fill; + display.IsSplitterFixed = true; + display.Location = new System.Drawing.Point(0, 47); + display.Name = "display"; // // display.Panel1 // - this.display.Panel1.Controls.Add(this.display2); + display.Panel1.Controls.Add(display2); // // display.Panel2 // - this.display.Panel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(30)))), ((int)(((byte)(30))))); - this.display.Size = new System.Drawing.Size(905, 488); - this.display.SplitterDistance = 322; - this.display.TabIndex = 4; - this.display.Resize += new System.EventHandler(this.Display_Resize); + display.Panel2.BackColor = System.Drawing.Color.FromArgb(30, 30, 30); + display.Size = new System.Drawing.Size(905, 488); + display.SplitterDistance = 322; + display.TabIndex = 4; + display.Resize += Display_Resize; // // display2 // - this.display2.BackColor = System.Drawing.Color.Transparent; - this.display2.Dock = System.Windows.Forms.DockStyle.Fill; - this.display2.IsSplitterFixed = true; - this.display2.Location = new System.Drawing.Point(0, 0); - this.display2.Name = "display2"; - this.display2.Orientation = System.Windows.Forms.Orientation.Horizontal; + display2.BackColor = System.Drawing.Color.Transparent; + display2.Dock = System.Windows.Forms.DockStyle.Fill; + display2.IsSplitterFixed = true; + display2.Location = new System.Drawing.Point(0, 0); + display2.Name = "display2"; + display2.Orientation = System.Windows.Forms.Orientation.Horizontal; // // display2.Panel1 // - this.display2.Panel1.Controls.Add(this.docBox); + display2.Panel1.Controls.Add(docBox); // // display2.Panel2 // - this.display2.Panel2.Controls.Add(this.fileView); - this.display2.Size = new System.Drawing.Size(322, 488); - this.display2.SplitterDistance = 241; - this.display2.TabIndex = 0; + display2.Panel2.Controls.Add(fileView); + display2.Size = new System.Drawing.Size(322, 488); + display2.SplitterDistance = 241; + display2.TabIndex = 0; // // docBox // - this.docBox.AutoCompleteBracketsList = new char[] { - '(', - ')', - '{', - '}', - '[', - ']', - '\"', - '\"', - '\'', - '\''}; - this.docBox.AutoIndentCharsPatterns = "\r\n^\\s*[\\w\\.]+(\\s\\w+)?\\s*(?=)\\s*(?[^;]+);\r\n"; - this.docBox.AutoScrollMinSize = new System.Drawing.Size(0, 50); - this.docBox.BackBrush = null; - this.docBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(30)))), ((int)(((byte)(30))))); - this.docBox.BracketsHighlightStrategy = FastColoredTextBoxNS.BracketsHighlightStrategy.Strategy2; - this.docBox.CharHeight = 14; - this.docBox.CharWidth = 7; - this.docBox.Cursor = System.Windows.Forms.Cursors.IBeam; - this.docBox.DisabledColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(180)))), ((int)(((byte)(180)))), ((int)(((byte)(180))))); - this.docBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.docBox.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.docBox.ForeColor = System.Drawing.Color.Gainsboro; - this.docBox.IndentBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(48))))); - this.docBox.IsReplaceMode = false; - this.docBox.LeftBracket = '('; - this.docBox.LeftBracket2 = '{'; - this.docBox.LineNumberColor = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240))))); - this.docBox.Location = new System.Drawing.Point(0, 0); - this.docBox.Name = "docBox"; - this.docBox.Paddings = new System.Windows.Forms.Padding(18); - this.docBox.ReadOnly = true; - this.docBox.RightBracket = ')'; - this.docBox.RightBracket2 = '}'; - this.docBox.SelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(255))))); - this.docBox.ServiceColors = ((FastColoredTextBoxNS.ServiceColors)(resources.GetObject("docBox.ServiceColors"))); - this.docBox.ShowLineNumbers = false; - this.docBox.Size = new System.Drawing.Size(322, 241); - this.docBox.TabIndex = 1; - this.docBox.TabStop = false; - this.docBox.WordWrap = true; - this.docBox.Zoom = 100; + docBox.AutoCompleteBracketsList = new char[] + { + '(', + ')', + '{', + '}', + '[', + ']', + '"', + '"', + '\'', + '\'' + }; + docBox.AutoIndentCharsPatterns = "\r\n^\\s*[\\w\\.]+(\\s\\w+)?\\s*(?=)\\s*(?[^;]+);\r\n"; + docBox.AutoScrollMinSize = new System.Drawing.Size(0, 50); + docBox.BackBrush = null; + docBox.BackColor = System.Drawing.Color.FromArgb(30, 30, 30); + docBox.BracketsHighlightStrategy = FastColoredTextBoxNS.BracketsHighlightStrategy.Strategy2; + docBox.CharHeight = 14; + docBox.CharWidth = 7; + docBox.Cursor = System.Windows.Forms.Cursors.IBeam; + docBox.DisabledColor = System.Drawing.Color.FromArgb(100, 180, 180, 180); + docBox.Dock = System.Windows.Forms.DockStyle.Fill; + docBox.Font = new System.Drawing.Font("Consolas", 9F); + docBox.ForeColor = System.Drawing.Color.Gainsboro; + docBox.Hotkeys = resources.GetString("docBox.Hotkeys"); + docBox.IndentBackColor = System.Drawing.Color.FromArgb(45, 45, 48); + docBox.IsReplaceMode = false; + docBox.LeftBracket = '('; + docBox.LeftBracket2 = '{'; + docBox.LineNumberColor = System.Drawing.Color.FromArgb(240, 240, 240); + docBox.Location = new System.Drawing.Point(0, 0); + docBox.Name = "docBox"; + docBox.Paddings = new System.Windows.Forms.Padding(18); + docBox.ReadOnly = true; + docBox.RightBracket = ')'; + docBox.RightBracket2 = '}'; + docBox.SelectionColor = System.Drawing.Color.FromArgb(60, 0, 0, 255); + docBox.ServiceColors = (FastColoredTextBoxNS.ServiceColors)resources.GetObject("docBox.ServiceColors"); + docBox.ShowLineNumbers = false; + docBox.Size = new System.Drawing.Size(322, 241); + docBox.TabIndex = 1; + docBox.TabStop = false; + docBox.WordWrap = true; + docBox.Zoom = 100; // // fileView // - this.fileView.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(30)))), ((int)(((byte)(30))))); - this.fileView.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.fileView.Dock = System.Windows.Forms.DockStyle.Fill; - this.fileView.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.fileView.Location = new System.Drawing.Point(0, 0); - this.fileView.Name = "fileView"; - this.fileView.Size = new System.Drawing.Size(322, 243); - this.fileView.TabIndex = 0; - this.fileView.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.fileView_NodeMouseClick); - this.fileView.NodeMouseDoubleClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.fileView_NodeMouseDoubleClick); + fileView.BackColor = System.Drawing.Color.FromArgb(30, 30, 30); + fileView.BorderStyle = System.Windows.Forms.BorderStyle.None; + fileView.Dock = System.Windows.Forms.DockStyle.Fill; + fileView.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); + fileView.Location = new System.Drawing.Point(0, 0); + fileView.Name = "fileView"; + fileView.Size = new System.Drawing.Size(322, 243); + fileView.TabIndex = 0; + fileView.NodeMouseClick += fileView_NodeMouseClick; + fileView.NodeMouseDoubleClick += fileView_NodeMouseDoubleClick; // // tabControl // - this.tabControl.Dock = System.Windows.Forms.DockStyle.Top; - this.tabControl.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.tabControl.Location = new System.Drawing.Point(0, 24); - this.tabControl.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); - this.tabControl.Name = "tabControl"; - this.tabControl.SelectedIndex = 0; - this.tabControl.Size = new System.Drawing.Size(905, 23); - this.tabControl.TabIndex = 5; - this.tabControl.SelectedIndexChanged += new System.EventHandler(this.tabControl_SelectedIndexChanged); - // - // clearProjectToolStripMenuItem - // - this.clearProjectToolStripMenuItem.Name = "clearProjectToolStripMenuItem"; - this.clearProjectToolStripMenuItem.Size = new System.Drawing.Size(260, 22); - this.clearProjectToolStripMenuItem.Text = "Clear Project"; - this.clearProjectToolStripMenuItem.Click += new System.EventHandler(this.clearProjectToolStripMenuItem_Click); - // - // toolStripMenuItem2 - // - this.toolStripMenuItem2.Name = "toolStripMenuItem2"; - this.toolStripMenuItem2.Size = new System.Drawing.Size(257, 6); - // - // toolStripMenuItem3 - // - this.toolStripMenuItem3.Name = "toolStripMenuItem3"; - this.toolStripMenuItem3.Size = new System.Drawing.Size(257, 6); - // - // toolStripMenuItem4 - // - this.toolStripMenuItem4.Name = "toolStripMenuItem4"; - this.toolStripMenuItem4.Size = new System.Drawing.Size(257, 6); + tabControl.Dock = System.Windows.Forms.DockStyle.Top; + tabControl.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); + tabControl.Location = new System.Drawing.Point(0, 24); + tabControl.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); + tabControl.Name = "tabControl"; + tabControl.SelectedIndex = 0; + tabControl.Size = new System.Drawing.Size(905, 23); + tabControl.TabIndex = 5; + tabControl.SelectedIndexChanged += tabControl_SelectedIndexChanged; // // GUI // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(30)))), ((int)(((byte)(30))))); - this.ClientSize = new System.Drawing.Size(905, 557); - this.Controls.Add(this.display); - this.Controls.Add(this.tabControl); - this.Controls.Add(this.statusStrip); - this.Controls.Add(this.menuStrip); - this.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MainMenuStrip = this.menuStrip; - this.Name = "GUI"; - this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; - this.Text = "DARKSCRIPT 3"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.GUI_FormClosing); - this.Load += new System.EventHandler(this.GUI_Load); - this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.GUI_KeyDown); - this.Move += new System.EventHandler(this.GUI_Move); - this.Resize += new System.EventHandler(this.GUI_Resize); - this.menuStrip.ResumeLayout(false); - this.menuStrip.PerformLayout(); - this.statusStrip.ResumeLayout(false); - this.statusStrip.PerformLayout(); - this.display.Panel1.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.display)).EndInit(); - this.display.ResumeLayout(false); - this.display2.Panel1.ResumeLayout(false); - this.display2.Panel2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.display2)).EndInit(); - this.display2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.docBox)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); - + AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + BackColor = System.Drawing.Color.FromArgb(30, 30, 30); + ClientSize = new System.Drawing.Size(905, 557); + Controls.Add(display); + Controls.Add(tabControl); + Controls.Add(statusStrip); + Controls.Add(menuStrip); + Font = new System.Drawing.Font("Consolas", 8.25F); + Icon = (System.Drawing.Icon)resources.GetObject("$this.Icon"); + MainMenuStrip = menuStrip; + Name = "GUI"; + SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; + Text = "DARKSCRIPT 3"; + FormClosing += GUI_FormClosing; + Load += GUI_Load; + KeyDown += GUI_KeyDown; + Move += GUI_Move; + Resize += GUI_Resize; + menuStrip.ResumeLayout(false); + menuStrip.PerformLayout(); + statusStrip.ResumeLayout(false); + statusStrip.PerformLayout(); + display.Panel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)display).EndInit(); + display.ResumeLayout(false); + display2.Panel1.ResumeLayout(false); + display2.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)display2).EndInit(); + display2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)docBox).EndInit(); + ResumeLayout(false); + PerformLayout(); } #endregion @@ -770,6 +738,9 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem4; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator7; + private System.Windows.Forms.ToolStripMenuItem showTooltipsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem useSmithboxForMetadataToolStripMenuItem; } } diff --git a/DarkScript3/GUI.cs b/DarkScript3/GUI.cs index 1ef44af..5acff82 100644 --- a/DarkScript3/GUI.cs +++ b/DarkScript3/GUI.cs @@ -78,9 +78,13 @@ private void GUI_Load(object sender, EventArgs e) SharedControls.SetGlobalFont(TextStyles.Font); // Ad-hoc way of doing settings, as tool menus (TODO: find something more permanent?) // Note this may call CheckChanged, which could have side effects + showTooltipsToolStripMenuItem.Checked = Settings.Default.DisplayTooltips; showArgumentsInTooltipToolStripMenuItem.Checked = Settings.Default.ArgTooltip; showArgumentsInPanelToolStripMenuItem.Checked = Settings.Default.ArgDocbox; - connectToolStripMenuItem.Checked = Settings.Default.UseSoapstone; + + connectToolStripMenuItem.Checked = Settings.Default.UseSoapstoneDSMS; + useSmithboxForMetadataToolStripMenuItem.Checked = Settings.Default.UseSoapstoneSmithbox; + // Update versions string previousVersion = Settings.Default.Version; if (!string.IsNullOrEmpty(previousVersion)) @@ -908,7 +912,15 @@ private void FileBrowserContextMenu_Opening(object sender, CancelEventArgs e) if (gameStr != null) { string mapName = tag.BaseName.Split('.')[0]; - ToolStripMenuItem mapItem = new ToolStripMenuItem($"Load {mapName} in DSMapStudio"); + + var name = "DSMapStudio"; + + if (DarkScript3.Properties.Settings.Default.UseSoapstoneSmithbox) + { + name = "Smithbox"; + } + + ToolStripMenuItem mapItem = new ToolStripMenuItem($"Load {mapName} in {name}"); mapItem.Click += async (sender, e) => await OpenFileBrowserMap(gameStr, mapName); FileBrowserContextMenu.Items.Add(mapItem); } @@ -1173,6 +1185,11 @@ private void checkForDarkScript3UpdatesToolStripMenuItem_Click(object sender, Ev { OpenURL("https://github.com/AinTunez/DarkScript3/releases"); } + private void showTooltipsToolStripMenuItem_CheckedChanged(object sender, EventArgs e) + { + Settings.Default.DisplayTooltips = showTooltipsToolStripMenuItem.Checked; + Settings.Default.Save(); + } private void showArgumentTooltipsToolStripMenuItem_CheckedChanged(object sender, EventArgs e) { @@ -1186,14 +1203,58 @@ private void showArgumentsInPanelToolStripMenuItem_CheckedChanged(object sender, Settings.Default.Save(); } + // DSMapStudio private void connectToolStripMenuItem_CheckedChanged(object sender, EventArgs e) { - Settings.Default.UseSoapstone = connectToolStripMenuItem.Checked; + if(connectToolStripMenuItem.Checked) + { + useSmithboxForMetadataToolStripMenuItem.Checked = false; + Settings.Default.UseSoapstoneDSMS = true; + Settings.Default.UseSoapstoneSmithbox = false; + } + else + { + useSmithboxForMetadataToolStripMenuItem.Checked = true; + Settings.Default.UseSoapstoneDSMS = false; + Settings.Default.UseSoapstoneSmithbox = true; + } + + Settings.Default.Save(); + SoapstoneMetadata metadata = SharedControls?.Metadata; + if (metadata != null && metadata.IsOpenable()) + { + if (Settings.Default.UseSoapstoneDSMS) + { + metadata.Open(); + } + else + { + metadata.Close(); + } + } + } + + // Smithbox + private void useSmithboxForMetadataToolStripMenuItem_CheckedChanged(object sender, EventArgs e) + { + if (useSmithboxForMetadataToolStripMenuItem.Checked) + { + connectToolStripMenuItem.Checked = false; + Settings.Default.UseSoapstoneDSMS = false; + Settings.Default.UseSoapstoneSmithbox = true; + } + else + { + connectToolStripMenuItem.Checked = true; + Settings.Default.UseSoapstoneDSMS = true; + Settings.Default.UseSoapstoneSmithbox = false; + } + Settings.Default.Save(); SoapstoneMetadata metadata = SharedControls?.Metadata; if (metadata != null && metadata.IsOpenable()) { - if (Settings.Default.UseSoapstone) + if (Settings.Default.UseSoapstoneSmithbox) { metadata.Open(); } @@ -1207,18 +1268,25 @@ private void connectToolStripMenuItem_CheckedChanged(object sender, EventArgs e) private void showConnectionInfoToolStripMenuItem_Click(object sender, EventArgs e) { StringBuilder sb = new StringBuilder(); - if (Settings.Default.UseSoapstone) + + var name = "DSMapStudio"; + if (Settings.Default.UseSoapstoneSmithbox) + { + name = "Smithbox"; + } + + if (Settings.Default.UseSoapstoneDSMS || Settings.Default.UseSoapstoneSmithbox) { - sb.AppendLine("DSMapStudio connectivity is enabled."); + sb.AppendLine($"{name} connectivity is enabled."); sb.AppendLine(); - sb.AppendLine("When DSMapStudio is open and Settings > Soapstone Server is enabled, " - + "data from DSMapStudio will be used to autocomplete values from params, FMGs, and loaded maps. " + sb.AppendLine($"When {name} is open and Settings > Soapstone Server is enabled, " + + $"data from {name} will be used to autocomplete values from params, FMGs, and loaded maps. " + "You can also hover on numbers in DarkScript3 to get tooltip info, " - + "and right-click on the tooltip to open it in DSMapStudio."); + + $"and right-click on the tooltip to open it in {name}."); } else { - sb.AppendLine("DSMapStudio connectivity is disabled."); + sb.AppendLine("Cross-reference connectivity is disabled."); sb.AppendLine(); if (connectToolStripMenuItem.Enabled) { @@ -1229,6 +1297,7 @@ private void showConnectionInfoToolStripMenuItem_Click(object sender, EventArgs sb.AppendLine($"Restart DarkScript3 and select \"{connectToolStripMenuItem.Text}\" to enable it."); } } + sb.AppendLine(); SoapstoneMetadata metadata = SharedControls.Metadata; string portStr = metadata.LastPort is int port ? $"{port}" : "None"; @@ -1237,14 +1306,21 @@ private void showConnectionInfoToolStripMenuItem_Click(object sender, EventArgs sb.AppendLine($"Client state: {metadata.State}"); sb.AppendLine(); sb.AppendLine(metadata.LastLoopResult ?? "No requests sent"); - ScrollDialog.Show(this, sb.ToString(), "DSMapStudio Soapstone Server Info"); + ScrollDialog.Show(this, sb.ToString(), "Soapstone Server Info"); } private void clearMetadataCacheToolStripMenuItem_Click(object sender, EventArgs e) { SharedControls.Metadata.ResetData(); + + var name = "DSMapStudio"; + if (Settings.Default.UseSoapstoneSmithbox) + { + name = "Smithbox"; + } + ScrollDialog.Show(this, - "Metadata cache cleared. Names and autocomplete items will be refetched from DSMapStudio when connected." + $"Metadata cache cleared. Names and autocomplete items will be refetched from {name} when connected." + "\n\n(This may be supported automatically in the future, if that would be helpful.)", "Cleared cached metadata"); } diff --git a/DarkScript3/GUI.resx b/DarkScript3/GUI.resx index 3fb1f99..7170020 100644 --- a/DarkScript3/GUI.resx +++ b/DarkScript3/GUI.resx @@ -1,4 +1,64 @@ + + @@ -63,6 +123,9 @@ 144, 17 + + Tab=IndentIncrease, Escape=ClearHints, PgUp=GoPageUp, PgDn=GoPageDown, End=GoEnd, Home=GoHome, Left=GoLeft, Up=GoUp, Right=GoRight, Down=GoDown, Insert=ReplaceMode, Del=DeleteCharRight, F3=FindNext, Shift+Tab=IndentDecrease, Shift+PgUp=GoPageUpWithSelection, Shift+PgDn=GoPageDownWithSelection, Shift+End=GoEndWithSelection, Shift+Home=GoHomeWithSelection, Shift+Left=GoLeftWithSelection, Shift+Up=GoUpWithSelection, Shift+Right=GoRightWithSelection, Shift+Down=GoDownWithSelection, Shift+Insert=Paste, Shift+Del=Cut, Ctrl+Back=ClearWordLeft, Ctrl+Space=AutocompleteMenu, Ctrl+End=GoLastLine, Ctrl+Home=GoFirstLine, Ctrl+Left=GoWordLeft, Ctrl+Up=ScrollUp, Ctrl+Right=GoWordRight, Ctrl+Down=ScrollDown, Ctrl+Insert=Copy, Ctrl+Del=ClearWordRight, Ctrl+0=ZoomNormal, Ctrl+A=SelectAll, Ctrl+B=BookmarkLine, Ctrl+C=Copy, Ctrl+E=MacroExecute, Ctrl+F=FindDialog, Ctrl+G=GoToDialog, Ctrl+H=ReplaceDialog, Ctrl+I=AutoIndentChars, Ctrl+M=MacroRecord, Ctrl+N=GoNextBookmark, Ctrl+R=Redo, Ctrl+U=UpperCase, Ctrl+V=Paste, Ctrl+X=Cut, Ctrl+Z=Undo, Ctrl+Add=ZoomIn, Ctrl+Subtract=ZoomOut, Ctrl+OemMinus=NavigateBackward, Ctrl+Shift+End=GoLastLineWithSelection, Ctrl+Shift+Home=GoFirstLineWithSelection, Ctrl+Shift+Left=GoWordLeftWithSelection, Ctrl+Shift+Right=GoWordRightWithSelection, Ctrl+Shift+B=UnbookmarkLine, Ctrl+Shift+C=CommentSelected, Ctrl+Shift+N=GoPrevBookmark, Ctrl+Shift+U=LowerCase, Ctrl+Shift+OemMinus=NavigateForward, Alt+Back=Undo, Alt+Up=MoveSelectedLinesUp, Alt+Down=MoveSelectedLinesDown, Alt+F=FindChar, Alt+Shift+Left=GoLeft_ColumnSelectionMode, Alt+Shift+Up=GoUp_ColumnSelectionMode, Alt+Shift+Right=GoRight_ColumnSelectionMode, Alt+Shift+Down=GoDown_ColumnSelectionMode + AAEAAAD/////AQAAAAAAAAAMAgAAAERGYXN0Q29sb3JlZFRleHRCb3gsIEN1bHR1cmU9bmV1dHJhbCwg diff --git a/DarkScript3/GameChooser.Designer.cs b/DarkScript3/GameChooser.Designer.cs index 7f101cc..c20caf9 100644 --- a/DarkScript3/GameChooser.Designer.cs +++ b/DarkScript3/GameChooser.Designer.cs @@ -28,161 +28,159 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.ds1Btn = new System.Windows.Forms.Button(); - this.bbBtn = new System.Windows.Forms.Button(); - this.ds3Btn = new System.Windows.Forms.Button(); - this.sekiroBtn = new System.Windows.Forms.Button(); - this.ds2Btn = new System.Windows.Forms.Button(); - this.ds2scholarBtn = new System.Windows.Forms.Button(); - this.fancy = new System.Windows.Forms.CheckBox(); - this.fancyLabel = new System.Windows.Forms.Label(); - this.customBtn = new System.Windows.Forms.Button(); - this.eldenBtn = new System.Windows.Forms.Button(); - this.SuspendLayout(); + ds1Btn = new System.Windows.Forms.Button(); + bbBtn = new System.Windows.Forms.Button(); + ds3Btn = new System.Windows.Forms.Button(); + sekiroBtn = new System.Windows.Forms.Button(); + ds2Btn = new System.Windows.Forms.Button(); + ds2scholarBtn = new System.Windows.Forms.Button(); + fancy = new System.Windows.Forms.CheckBox(); + fancyLabel = new System.Windows.Forms.Label(); + customBtn = new System.Windows.Forms.Button(); + eldenBtn = new System.Windows.Forms.Button(); + SuspendLayout(); // // ds1Btn // - this.ds1Btn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.ds1Btn.Location = new System.Drawing.Point(12, 12); - this.ds1Btn.Name = "ds1Btn"; - this.ds1Btn.Size = new System.Drawing.Size(141, 23); - this.ds1Btn.TabIndex = 0; - this.ds1Btn.Text = "Dark Souls"; - this.ds1Btn.UseVisualStyleBackColor = true; - this.ds1Btn.Click += new System.EventHandler(this.Ds1Btn_Click); + ds1Btn.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + ds1Btn.Location = new System.Drawing.Point(14, 14); + ds1Btn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + ds1Btn.Name = "ds1Btn"; + ds1Btn.Size = new System.Drawing.Size(164, 27); + ds1Btn.TabIndex = 0; + ds1Btn.Text = "Dark Souls"; + ds1Btn.UseVisualStyleBackColor = true; + ds1Btn.Click += Ds1Btn_Click; // // bbBtn // - this.bbBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.bbBtn.Location = new System.Drawing.Point(12, 41); - this.bbBtn.Name = "bbBtn"; - this.bbBtn.Size = new System.Drawing.Size(141, 23); - this.bbBtn.TabIndex = 1; - this.bbBtn.Text = "Bloodborne"; - this.bbBtn.UseVisualStyleBackColor = true; - this.bbBtn.Click += new System.EventHandler(this.BbBtn_Click); + bbBtn.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + bbBtn.Location = new System.Drawing.Point(14, 47); + bbBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + bbBtn.Name = "bbBtn"; + bbBtn.Size = new System.Drawing.Size(164, 27); + bbBtn.TabIndex = 1; + bbBtn.Text = "Bloodborne"; + bbBtn.UseVisualStyleBackColor = true; + bbBtn.Click += BbBtn_Click; // // ds3Btn // - this.ds3Btn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.ds3Btn.Location = new System.Drawing.Point(12, 127); - this.ds3Btn.Name = "ds3Btn"; - this.ds3Btn.Size = new System.Drawing.Size(141, 23); - this.ds3Btn.TabIndex = 2; - this.ds3Btn.Text = "Dark Souls III"; - this.ds3Btn.UseVisualStyleBackColor = true; - this.ds3Btn.Click += new System.EventHandler(this.Ds3Btn_Click); + ds3Btn.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + ds3Btn.Location = new System.Drawing.Point(14, 147); + ds3Btn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + ds3Btn.Name = "ds3Btn"; + ds3Btn.Size = new System.Drawing.Size(164, 27); + ds3Btn.TabIndex = 2; + ds3Btn.Text = "Dark Souls III"; + ds3Btn.UseVisualStyleBackColor = true; + ds3Btn.Click += Ds3Btn_Click; // // sekiroBtn // - this.sekiroBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.sekiroBtn.Location = new System.Drawing.Point(12, 156); - this.sekiroBtn.Name = "sekiroBtn"; - this.sekiroBtn.Size = new System.Drawing.Size(141, 23); - this.sekiroBtn.TabIndex = 3; - this.sekiroBtn.Text = "Sekiro"; - this.sekiroBtn.UseVisualStyleBackColor = true; - this.sekiroBtn.Click += new System.EventHandler(this.SekiroBtn_Click); + sekiroBtn.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + sekiroBtn.Location = new System.Drawing.Point(14, 180); + sekiroBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + sekiroBtn.Name = "sekiroBtn"; + sekiroBtn.Size = new System.Drawing.Size(164, 27); + sekiroBtn.TabIndex = 3; + sekiroBtn.Text = "Sekiro"; + sekiroBtn.UseVisualStyleBackColor = true; + sekiroBtn.Click += SekiroBtn_Click; // // ds2Btn // - this.ds2Btn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.ds2Btn.Location = new System.Drawing.Point(12, 70); - this.ds2Btn.Name = "ds2Btn"; - this.ds2Btn.Size = new System.Drawing.Size(141, 23); - this.ds2Btn.TabIndex = 4; - this.ds2Btn.Text = "Dark Souls II"; - this.ds2Btn.UseVisualStyleBackColor = true; - this.ds2Btn.Click += new System.EventHandler(this.ds2Btn_Click); + ds2Btn.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + ds2Btn.Location = new System.Drawing.Point(14, 81); + ds2Btn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + ds2Btn.Name = "ds2Btn"; + ds2Btn.Size = new System.Drawing.Size(164, 27); + ds2Btn.TabIndex = 4; + ds2Btn.Text = "Dark Souls II"; + ds2Btn.UseVisualStyleBackColor = true; + ds2Btn.Click += ds2Btn_Click; // // ds2scholarBtn // - this.ds2scholarBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.ds2scholarBtn.Location = new System.Drawing.Point(12, 99); - this.ds2scholarBtn.Name = "ds2scholarBtn"; - this.ds2scholarBtn.Size = new System.Drawing.Size(141, 23); - this.ds2scholarBtn.TabIndex = 5; - this.ds2scholarBtn.Text = "Dark Souls II SOTFS"; - this.ds2scholarBtn.UseVisualStyleBackColor = true; - this.ds2scholarBtn.Click += new System.EventHandler(this.ds2scholarBtn_Click); + ds2scholarBtn.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + ds2scholarBtn.Location = new System.Drawing.Point(14, 114); + ds2scholarBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + ds2scholarBtn.Name = "ds2scholarBtn"; + ds2scholarBtn.Size = new System.Drawing.Size(164, 27); + ds2scholarBtn.TabIndex = 5; + ds2scholarBtn.Text = "Dark Souls II SOTFS"; + ds2scholarBtn.UseVisualStyleBackColor = true; + ds2scholarBtn.Click += ds2scholarBtn_Click; // // fancy // - this.fancy.AutoSize = true; - this.fancy.Location = new System.Drawing.Point(12, 243); - this.fancy.Margin = new System.Windows.Forms.Padding(2); - this.fancy.Name = "fancy"; - this.fancy.Size = new System.Drawing.Size(96, 17); - this.fancy.TabIndex = 6; - this.fancy.Text = "Use MattScript"; - this.fancy.UseVisualStyleBackColor = true; - this.fancy.CheckedChanged += new System.EventHandler(this.fancy_CheckedChanged); + fancy.AutoSize = true; + fancy.Location = new System.Drawing.Point(14, 279); + fancy.Margin = new System.Windows.Forms.Padding(2); + fancy.Name = "fancy"; + fancy.Size = new System.Drawing.Size(103, 19); + fancy.TabIndex = 6; + fancy.Text = "Use MattScript"; + fancy.UseVisualStyleBackColor = true; + fancy.CheckedChanged += fancy_CheckedChanged; // // fancyLabel // - this.fancyLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.fancyLabel.Location = new System.Drawing.Point(9, 262); - this.fancyLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); - this.fancyLabel.Name = "fancyLabel"; - this.fancyLabel.Size = new System.Drawing.Size(144, 34); - this.fancyLabel.TabIndex = 7; - this.fancyLabel.Text = "(Formats scripts to make them easier to understand)"; + fancyLabel.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + fancyLabel.Location = new System.Drawing.Point(10, 301); + fancyLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + fancyLabel.Name = "fancyLabel"; + fancyLabel.Size = new System.Drawing.Size(168, 39); + fancyLabel.TabIndex = 7; + fancyLabel.Text = "(Formats scripts to make them easier to understand)"; // // customBtn // - this.customBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.customBtn.Location = new System.Drawing.Point(13, 213); - this.customBtn.Margin = new System.Windows.Forms.Padding(2); - this.customBtn.Name = "customBtn"; - this.customBtn.Size = new System.Drawing.Size(141, 26); - this.customBtn.TabIndex = 6; - this.customBtn.Text = "Custom EMEDF..."; - this.customBtn.UseVisualStyleBackColor = true; - this.customBtn.Click += new System.EventHandler(this.customBtn_Click); + customBtn.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + customBtn.Location = new System.Drawing.Point(14, 245); + customBtn.Margin = new System.Windows.Forms.Padding(2); + customBtn.Name = "customBtn"; + customBtn.Size = new System.Drawing.Size(164, 30); + customBtn.TabIndex = 6; + customBtn.Text = "Custom EMEDF..."; + customBtn.UseVisualStyleBackColor = true; + customBtn.Click += customBtn_Click; // // eldenBtn // - this.eldenBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.eldenBtn.Location = new System.Drawing.Point(12, 185); - this.eldenBtn.Name = "eldenBtn"; - this.eldenBtn.Size = new System.Drawing.Size(141, 23); - this.eldenBtn.TabIndex = 8; - this.eldenBtn.Text = "Elden Ring"; - this.eldenBtn.UseVisualStyleBackColor = true; - this.eldenBtn.Click += new System.EventHandler(this.eldenBtn_Click); + eldenBtn.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + eldenBtn.Location = new System.Drawing.Point(14, 213); + eldenBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + eldenBtn.Name = "eldenBtn"; + eldenBtn.Size = new System.Drawing.Size(164, 27); + eldenBtn.TabIndex = 8; + eldenBtn.Text = "Elden Ring"; + eldenBtn.UseVisualStyleBackColor = true; + eldenBtn.Click += eldenBtn_Click; // // GameChooser // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(165, 295); - this.Controls.Add(this.eldenBtn); - this.Controls.Add(this.fancyLabel); - this.Controls.Add(this.fancy); - this.Controls.Add(this.customBtn); - this.Controls.Add(this.ds2scholarBtn); - this.Controls.Add(this.ds2Btn); - this.Controls.Add(this.sekiroBtn); - this.Controls.Add(this.ds3Btn); - this.Controls.Add(this.bbBtn); - this.Controls.Add(this.ds1Btn); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; - this.KeyPreview = true; - this.Name = "GameChooser"; - this.Text = "GameChooser"; - this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.GameChooser_KeyDown); - this.ResumeLayout(false); - this.PerformLayout(); - + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(192, 355); + Controls.Add(eldenBtn); + Controls.Add(fancyLabel); + Controls.Add(fancy); + Controls.Add(customBtn); + Controls.Add(ds2scholarBtn); + Controls.Add(ds2Btn); + Controls.Add(sekiroBtn); + Controls.Add(ds3Btn); + Controls.Add(bbBtn); + Controls.Add(ds1Btn); + FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + KeyPreview = true; + Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + Name = "GameChooser"; + Text = "GameChooser"; + KeyDown += GameChooser_KeyDown; + ResumeLayout(false); + PerformLayout(); } #endregion diff --git a/DarkScript3/GameChooser.cs b/DarkScript3/GameChooser.cs index ca64b97..f381741 100644 --- a/DarkScript3/GameChooser.cs +++ b/DarkScript3/GameChooser.cs @@ -79,6 +79,12 @@ private void eldenBtn_Click(object sender, EventArgs e) Close(); } + private void ac6Btn_Click(object sender, EventArgs e) + { + SetResult("ac6-common.emedf.json"); + Close(); + } + private void GameChooser_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Escape) diff --git a/DarkScript3/GameChooser.resx b/DarkScript3/GameChooser.resx index c7e0d4b..b06a0bb 100644 --- a/DarkScript3/GameChooser.resx +++ b/DarkScript3/GameChooser.resx @@ -1,17 +1,17 @@ - diff --git a/DarkScript3/InstructionDocs.cs b/DarkScript3/InstructionDocs.cs index 7806800..3c5bf0a 100644 --- a/DarkScript3/InstructionDocs.cs +++ b/DarkScript3/InstructionDocs.cs @@ -732,7 +732,9 @@ private static List UnpackArgsSafe(Instruction ins, IEnumerable var result = new List(); using (var ms = new MemoryStream(ins.ArgData)) { - var br = new BinaryReaderEx(bigEndian, ms); + byte[] bytes = ms.ToArray(); + + var br = new BinaryReaderEx(bigEndian, bytes); foreach (ArgType arg in argStruct) { switch (arg) @@ -768,9 +770,9 @@ private static List UnpackArgsSafe(Instruction ins, IEnumerable private static void AssertZeroPad(BinaryReaderEx br, int align) { - if (br.Stream.Position % align > 0) + if (br.Position % align > 0) { - br.AssertPattern(align - (int)(br.Stream.Position % align), 0); + br.AssertPattern(align - (int)(br.Position % align), 0); } } diff --git a/DarkScript3/Properties/Settings.Designer.cs b/DarkScript3/Properties/Settings.Designer.cs index c3db2bb..cea0f17 100644 --- a/DarkScript3/Properties/Settings.Designer.cs +++ b/DarkScript3/Properties/Settings.Designer.cs @@ -45,8 +45,21 @@ public bool DefaultFancy { set { this["WindowPosition"] = value; } - } - + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool DisplayTooltips + { + get { + return ((bool)(this["DisplayTooltips"])); + } + set { + this["DisplayTooltips"] = value; + } + } + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -71,18 +84,31 @@ public bool ArgDocbox { } } + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool UseSoapstoneDSMS { + get { + return ((bool)(this["UseSoapstoneDSMS"])); + } + set { + this["UseSoapstoneDSMS"] = value; + } + } + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool UseSoapstone { + public bool UseSoapstoneSmithbox + { get { - return ((bool)(this["UseSoapstone"])); + return ((bool)(this["UseSoapstoneSmithbox"])); } set { - this["UseSoapstone"] = value; + this["UseSoapstoneSmithbox"] = value; } - } - + } + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("")] diff --git a/DarkScript3/SharedControls.cs b/DarkScript3/SharedControls.cs index c93f2b1..27ac822 100644 --- a/DarkScript3/SharedControls.cs +++ b/DarkScript3/SharedControls.cs @@ -40,7 +40,7 @@ public SharedControls(GUI gui, ToolStripStatusLabel statusLabel, FastColoredText this.docBox = docBox; NameMetadata = nameMetadata; Metadata = new SoapstoneMetadata(nameMetadata); - if (Settings.Default.UseSoapstone) + if (Settings.Default.UseSoapstoneDSMS || Settings.Default.UseSoapstoneSmithbox) { Metadata.Open(); } diff --git a/DarkScript3/SoapstoneMetadata.cs b/DarkScript3/SoapstoneMetadata.cs index 29ab61e..5a4e21e 100644 --- a/DarkScript3/SoapstoneMetadata.cs +++ b/DarkScript3/SoapstoneMetadata.cs @@ -7,6 +7,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using DarkScript3.Properties; using SoapstoneLib; using SoapstoneLib.Proto; using static DarkScript3.DocAutocomplete; @@ -80,7 +81,15 @@ public void Open() } if (State == ClientState.Closed) { - provider.Server = KnownServer.DSMapStudio; + if (Settings.Default.UseSoapstoneSmithbox) + { + provider.Server = KnownServer.Smithbox; + } + if (Settings.Default.UseSoapstoneDSMS) + { + provider.Server = KnownServer.DSMapStudio; + } + State = ClientState.Open; } } diff --git a/SoapstoneLib/.gitignore b/SoapstoneLib/.gitignore new file mode 100644 index 0000000..8a30d25 --- /dev/null +++ b/SoapstoneLib/.gitignore @@ -0,0 +1,398 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml diff --git a/SoapstoneLib/LICENSE b/SoapstoneLib/LICENSE new file mode 100644 index 0000000..911aabf --- /dev/null +++ b/SoapstoneLib/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022 thefifthmatt + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/SoapstoneLib/Proto/common.proto b/SoapstoneLib/Proto/common.proto new file mode 100644 index 0000000..942a731 --- /dev/null +++ b/SoapstoneLib/Proto/common.proto @@ -0,0 +1,121 @@ +syntax = "proto3"; + +package SoapstoneLib.Proto; + +// +// Public requests +// + +message ServerInfoRequest { +} + +message ServerInfoResponse { + // Client id like "DSMapStudio" + string id = 1; + // Version like "1.0.0" + string version = 2; + // File name of the server exe + string server_path = 3; + // All open resources + repeated EditorResource resources = 4; +} + +// +// Public common messages +// + +// A thing which can be opened in an editor, scoped to a particular game and/or mod project. +// Using this generally requires knowing which editor you're talking with, as different editors +// will load different things simultaneously. +// A game object can be available in multiple different resources at once. For instance, there may +// be a live view of an opened file, but also a passive index of the same data, which could be different +// (potentially overlapping) resources. Err on the side of having a bunch of these. +message EditorResource { + // The type of resource. + EditorResourceType type = 1; + // A game enum, checked by the client for basic compatibility + // Most other objects don't specify a game, so they're largely context-dependent. + FromSoftGame game = 2; + // Unique identifier for the resource, if there is more than one of a given type. + // For params in DSMS, this can be left blank, as all params are opened together. + // For FMGs, this could be the language. + // For a map in DSMS, this could be a name like "m10_00_00_00", or it can be left blank to + // mean all open maps. + // In editors which are not project-based, this could be the entire file or directory path. + string name = 3; + // Optional, a project.json file, in the root directory of the mod folder. + // The definition should be moved into a common library if this is used in multiple editors. + // https://github.com/soulsmods/DSMapStudio/blob/master/StudioCore/Editor/ProjectSettings.cs + string project_json_path = 4; + // Optional, some other kind of filesystem path representing the resource. + // This is mainly for editors which don't care about game hierarchy and edit individual files. + string resource_path = 5; +} + +enum EditorResourceType { + // By convention, the default 0 field of all enums should be an "unset" value. + // This should never be set on purpose. + EDITOR_RESOURCE_TYPE_UNSPECIFIED = 0; + EDITOR_RESOURCE_TYPE_PROJECT = 1; + EDITOR_RESOURCE_TYPE_PARAM = 2; + EDITOR_RESOURCE_TYPE_FMG = 3; + EDITOR_RESOURCE_TYPE_MAP = 4; + // Extend this in the future. Possible future ideas: + // - Alias resource, for statically loaded names for maps, models, etc. + // - Model resource, for a model editor + // - Metadata resource, for passively indexed metadata +} + +// Enum for supported games. This can be extended based on what editors support. +enum FromSoftGame { + FROM_SOFT_GAME_UNSPECIFIED = 0; + DEMONS_SOULS = 1; + DARK_SOULS_PTDE = 2; + DARK_SOULS_REMASTERED = 3; + DARK_SOULS_2 = 4; + DARK_SOULS_2_SOTFS = 5; + BLOODBORNE = 6; + DARK_SOULS_3 = 7; + SEKIRO = 8; + ELDEN_RING = 9; +} + +// Namespaces for different types of game data. +// These do not uniquely identify every type of game object (like objects vs enemies), +// and is meant for cases when the main ids can overlap for two objects in the same file. +// Use object properties for specific subtype data. +enum KeyNamespace { + KEY_NAMESPACE_UNSPECIFIED = 0; + // An MSB event. Identical names are disambiguated using {2} etc suffixes + KEY_NAMESPACE_MAP_EVENT = 1; + // An MSB region. Identical names are disambiguated using {2} etc suffixes + KEY_NAMESPACE_MAP_REGION = 2; + // An MSB part. Identical names are disambiguated using {2} etc suffixes + KEY_NAMESPACE_MAP_PART = 3; +} + +// The standard FromSoft comparison operators, and also regex ~ and !~ +enum PropertyComparisonType { + PROPERTY_COMPARISON_TYPE_UNSPECIFIED = 0; + EQUAL = 1; + NOT_EQUAL = 2; + GREATER = 3; + LESS = 4; + GREATER_OR_EQUAL = 5; + LESS_OR_EQUAL = 6; + MATCHES = 7; + NOT_MATCHES = 8; +} + +// Used for get/search operations to determine which properties to retrieve. +message RequestedProperty { + // Like GameProperty, this is a simple string key for the moment. + string key = 1; + // Whether to include this property only when it is >0 or non-null. + // This is largely to avoid a ton of useless EntityID/EntityGroupIDs values. + bool non_trivial_only = 2; +} + +message SearchOptions { + int32 max_results = 1; +} diff --git a/SoapstoneLib/Proto/internal.proto b/SoapstoneLib/Proto/internal.proto new file mode 100644 index 0000000..66ef771 --- /dev/null +++ b/SoapstoneLib/Proto/internal.proto @@ -0,0 +1,188 @@ +syntax = "proto3"; + +import "common.proto"; + +package SoapstoneLib.Proto.Internal; + +// +// Internal requests (prefer to use library instead of accessing these directly) +// + +message SearchObjectsRequest { + EditorResource resource = 1; + PrimaryKeyType result_type = 2; + PropertySearch search = 3; + repeated RequestedProperty properties = 4; + SearchOptions options = 5; +} + +message SearchObjectsResponse { + repeated GameObject results = 1; +} + +message GetObjectRequest { + EditorResource resource = 1; + PrimaryKey key = 2; + repeated RequestedProperty properties = 3; +} + +message GetObjectResponse { + GameObject result = 1; +} + +message BatchGetObjectsRequest { + EditorResource resource = 1; + repeated PrimaryKey keys = 2; + repeated RequestedProperty properties = 3; +} + +message BatchGetObjectsResponse { + repeated GameObject results = 1; +} + +message OpenResourceRequest { + EditorResource resource = 1; +} + +message OpenResourceResponse { +} + +message OpenObjectRequest { + EditorResource resource = 1; + PrimaryKey key = 2; +} + +message OpenObjectResponse { +} + +message OpenSearchRequest { + EditorResource resource = 1; + PrimaryKeyType result_type = 2; + PropertySearch search = 3; + // Likewise, editors can do this if supported + bool open_first_result = 4; +} + +message OpenSearchResponse { +} + +// Internal SoapstoneLib stuff. Clients should use public APIs instead + +message GameObject { + // Identifier for the object, to use for subsequent UI operations + PrimaryKey key = 1; + // All requested properties. + // The same property name can repeat multiple times, like EntityGroupIDs. + // Alternatively, PropertyValue could support arrays. + repeated GameProperty properties = 2; +} + +// Some relatively unique identifier for a game object: the file it's in and an id within it. +// Clients should not examine this directly. Prefer to use properties. +// Note that EntityID is *not* a valid primary key. It would be a property of an entity. +// Clients should avoid constructing this key object directly. Instead, use high-level methods in +// a C# library to ensure that different editors use the same representation for game objects. +// Failing that, try to use keys returned by servers, rather than constructing them in clients. +// It is possible for key representations to change over time (and maybe even proto fields), but +// try to maintain backwards compatibility if it's not too arduous. +// This system is meant for data in straightforward key-value pairs, not complex recursive data. +message PrimaryKey { + // The lowercase name of the file that this game object appears in, after the interrot prefix + // (or other redundant prefixes if no interroot), and without the dcx extension. Overall, it + // should be reasonably stable across games. + // This might be "map/mapstudio/m10_00_00_00.msb" for a map, which is a top-level file. + // For a resource within a BND, this would be the path name, like + // "script/talk/m10_00_00_00/t204101000.esd" or "chr/c0000/tae/a00.tae" or + // "param/GameParam/ThrowParam.param". + // Note that protobuf interns strings while deserializing, so this is fairly cheap, but if + // necessary we can do some kind of reference table or RLE-type compression. + string file = 1; + // Optionally, a namespace within a file for scoping ids. For instance, in MSB, names + // are disambiguated only within regions/parts. FMGs don't need this - all ids are entry ids. + // Feel free to create dozens of these as appropriate for disambiguation purposes, but don't + // rely on this for precise type information (e.g. distinguishing enemies and objects) - + // use properties for that. + KeyNamespace namespace = 2; + // If there are multiple instances of something with the same id, like a param row for + // instance, this identifies which one it is, starting from 0, then 1, etc. + // This number should be stable but is not otherwise meaningful. + int32 index = 3; + // The main stable identifier for the object, scoped within the file and namespace. + // Generally, this should represent how the object is referenced within the format. + // If the object has a numerical identifier, use that. If it has a name field, use that. + // Try to avoid indices if there is a more stable alternative. + // If there is a multipart numerical id, separate parts using underscores. + // If this field is missing, this primary key can be used to mean the entire file. + oneof id { + // String name, or specially formatted name + string str_id = 4; + // Integer id (any width) + int64 int_id = 5; + } +} + +// An internal representation of a type of a PrimaryKey object. +// This is mainly for filtering search results. +message PrimaryKeyType { + // A template version of the PrimaryKey file key, using {0} {1} by convention. + // For instance, using the examples given in PrimaryKey, the types would be + // "script/talk/{0}/{1}.esd" or "chr/{0}/tae/{1}.tae" or "param/GameParam/{0}.param". + string file = 1; + // The PrimaryKey namespace. Must not be unspecified. + PrimaryKeyCategory category = 2; +} + +enum PrimaryKeyCategory { + PRIMARY_KEY_CATEGORY_UNSPECIFIED = 0; + // A game file with a unique path. This includes both bnd files and files within bnds. + PRIMARY_KEY_CATEGORY_FILE = 1; + // A key-value entry in a game file. + PRIMARY_KEY_CATEGORY_ENTRY = 2; + +} + +message GameProperty { + // For the moment, use a string key here. This could become more richly typed in the future. + string key = 1; + PropertyValue value = 2; +} + +// This is a wrapper for scalar values supported by SearchProperties. +// The oneof includes a case enum by default, plus the value itself. +// shorts/bytes are represented using varint 32-bit values, as they have no special proto type. +message PropertyValue { + oneof value { + // This is quite a few values, but tag numbers are kept in one byte (<2^4, protobuf-net + // tag number performance) and it's stored with low overhead (private object value_). + sint32 s8_value = 1; + uint32 u8_value = 2; + sint32 s16_value = 3; + uint32 u16_value = 4; + sint32 s32_value = 5; + uint32 u32_value = 6; + sint64 s64_value = 7; + uint64 u64_value = 8; + float f32_value = 9; + double f64_value = 10; + bool bool_value = 11; + string string_value = 12; + } +} + +// Representation of a search. +// For anything more complex, an expression language can be added later. +message PropertySearch { + // Conjunctive normal form (AND of ORs) + repeated MultiPropertyCondition all_of_any_conditions = 1; +} + +message MultiPropertyCondition { + repeated PropertyCondition conditions = 3; +} + +// Some basic search condition like EntityID == 5555 +message PropertyCondition { + PropertyComparisonType type = 1; + string key = 2; + PropertyValue value = 3; +} diff --git a/SoapstoneLib/Proto/soapstone.proto b/SoapstoneLib/Proto/soapstone.proto new file mode 100644 index 0000000..761e3a7 --- /dev/null +++ b/SoapstoneLib/Proto/soapstone.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +import "common.proto"; +import "internal.proto"; + +package SoapstoneLib.Proto.Internal; + +service Soapstone { + // Returns basic info about the editor's current state. All servers must implement this. + // Editor resources are parts of an editor which can be individually loaded or unloaded. + // Once a resource is loaded, various objects and functionality can be straightforwardly accessed within it. + rpc GetServerInfo (ServerInfoRequest) returns (ServerInfoResponse); + + // Get objects matching a search query, with requested properties. + // A property does not have to be requested to search against it. + rpc SearchObjects (Internal.SearchObjectsRequest) returns (Internal.SearchObjectsResponse); + + // Returns a single object, within an editor resource type. + // This can be used if the exact key is known, or to get more properties after a broader search. + // It returns empty if object is not found (as opposed to an error). + rpc GetObject (Internal.GetObjectRequest) returns (Internal.GetObjectResponse); + + // Returns objects in batch, within an editor resource type. + // It returns only the objects which could be found. + rpc BatchGetObjects (Internal.BatchGetObjectsRequest) returns (Internal.BatchGetObjectsResponse); + + // Open a resource, like a map by name. + rpc OpenResource (Internal.OpenResourceRequest) returns (Internal.OpenResourceResponse); + + // Jump to or frame the given object within the editor. + rpc OpenObject (Internal.OpenObjectRequest) returns (Internal.OpenObjectResponse); + + // Start a given search in the editor. + rpc OpenSearch (Internal.OpenSearchRequest) returns (Internal.OpenSearchResponse); +} diff --git a/SoapstoneLib/README.md b/SoapstoneLib/README.md new file mode 100644 index 0000000..69b4d39 --- /dev/null +++ b/SoapstoneLib/README.md @@ -0,0 +1,37 @@ +# SoapstoneLib + +SoapstoneLib is a library for FromSoft editor programs to communicate with each other via localhost RPCs. It allows invoking parts of a "server" editor from "client" editors, like clicking on an entity ID in a script editor program and jumping to that entity in a separate map editor program. As part of this, basic metadata about game objects and overall project setup info can also be communicated. In the future, RPC functionality can be expanded for whatever runtime communication editors may need, like multi-step import/export flows. + +This is not meant to be a universal solution for editor interoperability. If a program can be invoked on command line or incorporated as a library, that may be preferable. Likewise, editors can share game files, or even custom metadata files, and concurrently watch for modifications to those files. + +`project.json` files are one possible standard for editors to share info about projects and the game directories they use. It is supported by Soapstone: you can connect to another program and get all of its currently open projects and their `project.json` paths. It is not required for servers or clients to support it, and other similar standards may be supported. + +**Do not use Soapstone functionality unless your project is fully source-available and you'd be able to accept contributions from soulsmods contributors.** Depending on future use cases, it may be necessary to make backwards-incompatible changes to clients and servers, and this is not possible unless all existing usages can be viewed and migrated. + +As of 2022, this library is maintained by thefifthmatt. Please get in touch if you have any possible use cases which could be supported. + +## Using the library + +Editors can set up a server via `SoapstoneServer.RunAsync`, which starts up a Kestrel-based gRPC server, and they can create a client using `SoapstoneClient.GetProvider`. Both of these take a `KnownServer` object which has well-known ports together with a server process name for netstat-based lookup. Clients can use these to seamlessly adapt to servers going up or down, corresponding to editors opening or closing. + +The RPC service itself does not hardcode any game object types, so any two programs can communicate any type of object, so long as they agree on a key format. The `SoulsKey` file defines some of these key formats using wrapper C# classes. The intention is for more key classes to be added as more game objects are supported. It is currently a closed set. `SoulsObject` allows representing individual instances of objects with a flat set of key-value properties. + +FMGs are a special case. The standard key format uses two custom enums, `FmgType` and `FmgLanguage`. This is because there is no unique key for FMGs which is applicable to all games, so `SoulsFmg` provides ways to look up custom enums from in-game keys, like file names and binder ids, and vice versa. + +It is assumed that editors consist of *resources*, which in turn contain *game objects*. In general terms, a resource is a file (or set of files) which can be opened in the editor. Different map files may be different resources if they can be loaded and unloaded independently, but item and menu FMGs may be part of the same resource if they are always loaded together. Some types of game objects may be present in multiple resources at once. For instance, a character model could be loaded as a chrbnd file, but a list of human-readable character model names could be available separately even when no models are loaded. + +## Supporting other programming languages + +Soapstone can support any language supported by gRPC, not just C#. This includes C++, Go, Java, Python, and more. + +Wrapper libraries are not strictly needed to use Soapstone, but creating a library with shared key formats and FMG data is recommended. Please add these as separate subdirectories in this repository. + +## Supporting new servers + +The initial planned server for Soapstone is DSMapStudio. However, any editor which is authoritative for some set of formats can be a server. If you do this, add a field in `KnownServer` with the next port number, and add any `EditorResourceType`s and key formats necessary to represent desired interactions. + +It's not unreasonable for an editor program to be both a server and a client, if it is authoritative for one format and contains secondary references to a different format. Separately, bidirectional streaming RPCs may be possible in the future, which would be preferable for cases like listening to server changes from a client. + +## Supporting new clients + +You can use this library in editors for runtime connection features, but make sure you're in contact with the server maintainer first so they can support whatever features you need, and can adapt to changes in those features over time. As above, only do this if your program is source-available. diff --git a/SoapstoneLib/SoapstoneLib.sln b/SoapstoneLib/SoapstoneLib.sln new file mode 100644 index 0000000..07efa1c --- /dev/null +++ b/SoapstoneLib/SoapstoneLib.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31912.275 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SoapstoneLib", "SoapstoneLib\SoapstoneLib.csproj", "{EDA4DF71-7052-4FD7-A1D0-537C54A88F76}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsoleApp", "TestConsoleApp\TestConsoleApp.csproj", "{84E88C5C-67FD-480B-A239-D1F492C7A48F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SoulsFormats", "..\SoulsFormats\SoulsFormats\SoulsFormats.csproj", "{37536028-5402-4C1C-9330-60F14E324589}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EDA4DF71-7052-4FD7-A1D0-537C54A88F76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDA4DF71-7052-4FD7-A1D0-537C54A88F76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDA4DF71-7052-4FD7-A1D0-537C54A88F76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDA4DF71-7052-4FD7-A1D0-537C54A88F76}.Release|Any CPU.Build.0 = Release|Any CPU + {84E88C5C-67FD-480B-A239-D1F492C7A48F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {84E88C5C-67FD-480B-A239-D1F492C7A48F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84E88C5C-67FD-480B-A239-D1F492C7A48F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {84E88C5C-67FD-480B-A239-D1F492C7A48F}.Release|Any CPU.Build.0 = Release|Any CPU + {37536028-5402-4C1C-9330-60F14E324589}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37536028-5402-4C1C-9330-60F14E324589}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37536028-5402-4C1C-9330-60F14E324589}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37536028-5402-4C1C-9330-60F14E324589}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2026C74E-D303-4104-A8DC-CB94110BDE17} + EndGlobalSection +EndGlobal diff --git a/SoapstoneLib/SoapstoneLib/.editorconfig b/SoapstoneLib/SoapstoneLib/.editorconfig new file mode 100644 index 0000000..5b7c5ae --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/.editorconfig @@ -0,0 +1,14 @@ +# You can modify the rules from these initially generated values to suit your own policies +# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference + +end_of_line = lf + +[*.cs] +indent_style = space + +[*.proto] +indent_style = space +indent_size = 2 + +[*.{cs,vb}] +end_of_line=lf \ No newline at end of file diff --git a/SoapstoneLib/SoapstoneLib/InternalConversions.cs b/SoapstoneLib/SoapstoneLib/InternalConversions.cs new file mode 100644 index 0000000..7e2153e --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/InternalConversions.cs @@ -0,0 +1,286 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using SoapstoneLib.Proto; +using SoapstoneLib.Proto.Internal; +using static SoapstoneLib.Proto.Internal.PropertyValue; + +namespace SoapstoneLib +{ + internal static class InternalConversions + { + private static readonly LRACache fileKeyCache = new LRACache(2000); + + internal static GameObject ToGameObject(SoulsObject obj) + { + return new GameObject + { + Key = ToPrimaryKey(obj.Key), + Properties = { obj.properties }, + }; + } + + internal static SoulsObject FromGameObject(GameObject obj) + { + return new SoulsObject(FromPrimaryKey(obj.Key), obj.Properties); + } + + internal static PrimaryKey ToPrimaryKey(SoulsKey key) + { + PrimaryKey ret = new PrimaryKey + { + File = key.File.FileName, + }; + if (key is SoulsKey.ObjectKey objKey) + { + object id = objKey.InternalID; + if (id is string strId) + { + ret.StrId = strId; + } + else + { + // This may throw if the id is not int64-compatible. + // Currently, these are all contained within SoapstoneLib, so this should not happen. + ret.IntId = Convert.ToInt64(id); + } + // Because this is proto3, setting default values should be a no-op + ret.Namespace = objKey.Namespace; + ret.Index = objKey.Index; + } + return ret; + } + + internal static SoulsKey FromPrimaryKey(PrimaryKey key) + { + if (!fileKeyCache.TryGetValue(key.File, out SoulsKey.FileKey file)) + { + file = SoulsKey.ParseFileKey(key.File); + fileKeyCache.Add(key.File, file); + } + if (key.IdCase == PrimaryKey.IdOneofCase.None) + { + return file; + } + if (file is SoulsKey.GameParamKey gameParamKey + && key.Namespace == KeyNamespace.Unspecified + && key.IdCase == PrimaryKey.IdOneofCase.IntId) + { + return new SoulsKey.GameParamRowKey(gameParamKey, Convert.ToInt32(key.IntId), key.Index); + } + else if (file is SoulsKey.MsbKey msbKey + && SoulsKey.MsbEntryKey.Namespaces.Contains(key.Namespace) + && key.IdCase == PrimaryKey.IdOneofCase.StrId) + { + return new SoulsKey.MsbEntryKey(msbKey, key.Namespace, key.StrId); + } + else if (file is SoulsKey.FmgKey fmgKey + && key.Namespace == KeyNamespace.Unspecified + && key.IdCase == PrimaryKey.IdOneofCase.IntId) + { + return new SoulsKey.FmgEntryKey(fmgKey, Convert.ToInt32(key.IntId), key.Index); + } + return new SoulsKey.UnknownKey(); + } + + internal static PrimaryKeyType ToPrimaryKeyType(SoulsKeyType t) + { + return new PrimaryKeyType { File = t.File, Category = t.Category }; + } + + // At the moment, this is a sealed collection only from SoulsKey. + // More flexibility can probably be added at the API level within SoulsKey, if necessary. + internal static bool FromPrimaryKeyType(PrimaryKeyType type, out SoulsKeyType result) + { + // Create an invalid instance for matching purposes + SoulsKeyType cand = new SoulsKeyType(type.File, type.Category, null); + // This lookup may fail if there is no match. + // This would mainly happen if a server is older than a client and supports fewer types. + return SoulsKey.KeyTypes.TryGetValue(cand, out result); + } + + internal static GameProperty ToGameProperty(SoulsObject.Property prop) + { + return new GameProperty { Key = prop.Key, Value = ToPropertyValue(prop.Value) }; + } + + internal static SoulsObject.Property FromGameProperty(GameProperty prop) + { + return new SoulsObject.Property(prop.Key, FromPropertyValue(prop.Value)); + } + + internal static Proto.Internal.PropertySearch ToPropertySearch(PropertySearch search) + { + // Blindly fail on null values here + return new Proto.Internal.PropertySearch + { + AllOfAnyConditions = { search.AllOfAnyConditions.Select(ToMultiPropertyCondition) }, + }; + } + + internal static PropertySearch FromPropertySearch(Proto.Internal.PropertySearch search) + { + return new PropertySearch(search.AllOfAnyConditions.Select(FromMultiPropertyCondition).ToList()); + } + + internal static MultiPropertyCondition ToMultiPropertyCondition(IEnumerable cond) + { + return new MultiPropertyCondition + { + Conditions = { cond.Select(ToPropertyCondition) }, + }; + } + + internal static List FromMultiPropertyCondition(MultiPropertyCondition cond) + { + return cond.Conditions.Select(FromPropertyCondition).ToList(); + } + + internal static PropertyCondition ToPropertyCondition(PropertySearch.Condition cond) + { + return new PropertyCondition + { + Type = cond.Type, + Key = cond.Key, + Value = ToPropertyValue(cond.Value), + }; + } + + internal static PropertySearch.Condition FromPropertyCondition(PropertyCondition cond) + { + return new PropertySearch.Condition(cond.Type, cond.Key, FromPropertyValue(cond.Value)); + } + + internal static PropertyValue ToPropertyValue(object obj) + { + PropertyValue value = new PropertyValue(); + switch (obj) + { + case sbyte s8Value: + value.S8Value = s8Value; + break; + case byte u8Value: + value.U8Value= u8Value; + break; + case short s16Value: + value.S16Value = s16Value; + break; + case ushort u16Value: + value.U16Value = u16Value; + break; + case int s32Value: + value.S32Value = s32Value; + break; + case uint u32Value: + value.U32Value = u32Value; + break; + case long s64Value: + value.S64Value = s64Value; + break; + case ulong u64Value: + value.U64Value = u64Value; + break; + case float f32Value: + value.F32Value = f32Value; + break; + case double f64Value: + value.F64Value = f64Value; + break; + case bool boolValue: + value.BoolValue = boolValue; + break; + case string stringValue: + value.StringValue = stringValue; + break; + case Regex regexValue: + // Special case for regexes, mainly in Conditions + value.StringValue = regexValue.ToString(); + break; + default: + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + throw new Exception($"Invalid property type {obj.GetType()} (only standard scalar built-in value types and strings are supported)"); + } + return value; + } + + internal static object FromPropertyValue(PropertyValue value) + { + switch (value.ValueCase) + { + case ValueOneofCase.S8Value: + return value.S8Value; + case ValueOneofCase.U8Value: + return value.U8Value; + case ValueOneofCase.S16Value: + return value.S16Value; + case ValueOneofCase.U16Value: + return value.U16Value; + case ValueOneofCase.S32Value: + return value.S32Value; + case ValueOneofCase.U32Value: + return value.U32Value; + case ValueOneofCase.S64Value: + return value.S64Value; + case ValueOneofCase.U64Value: + return value.U64Value; + case ValueOneofCase.F32Value: + return value.F32Value; + case ValueOneofCase.F64Value: + return value.F64Value; + case ValueOneofCase.BoolValue: + return value.BoolValue; + case ValueOneofCase.StringValue: + return value.StringValue; + default: + throw new ArgumentException($"Invalid PropertyValue {value}"); + } + } + + /// + /// Very simple "least recently added" cache with a mechanism to avoid unbounded growth. + /// + /// This attempts to enable sharing of values without the full invasive pointer logic + /// of an least recently updated/accessed cache. In the worst case, looping over a big list + /// of keys larger than the capacity, most types of caches perform poorly. Overall, this + /// system works best with both time locality and a fairly cheap cost of replacing lost + /// entries, so is suitable for storing small objects deterministically given by the key. + /// + private class LRACache + { + private readonly int capacity; + private readonly Dictionary inner = new Dictionary(); + // Could also use an array-based ring buffer here, check traces for time/space tradeoffs. + private readonly LinkedList order = new LinkedList(); + + public LRACache(int capacity) + { + this.capacity = capacity; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public bool TryGetValue(K key, out V value) => inner.TryGetValue(key, out value); + + [MethodImpl(MethodImplOptions.Synchronized)] + public void Add(K key, V value) + { + if (inner.ContainsKey(key)) + { + inner[key] = value; + return; + } + if (inner.Count >= capacity) + { + inner.Remove(order.First.Value); + order.RemoveFirst(); + } + inner[key] = value; + order.AddLast(key); + } + } + } +} diff --git a/SoapstoneLib/SoapstoneLib/KnownServer.cs b/SoapstoneLib/SoapstoneLib/KnownServer.cs new file mode 100644 index 0000000..2dd89c6 --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/KnownServer.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; + +namespace SoapstoneLib +{ + /// + /// Connection info used by both locally running servers and by local clients, so they can find each other. + /// + /// Currently, this only supports localhost connections. + /// + public sealed class KnownServer + { + /// + /// Standard server info for DSMapStudio. + /// + public static readonly KnownServer DSMapStudio = new KnownServer(22720, "DSMapStudio"); + + /// + /// Standard server info for Smithbox. + /// + public static readonly KnownServer Smithbox = new KnownServer(22721, "Smithbox"); + + /// + /// Expected local process name of this server. This usually matches the exe name. + /// + public string ProcessName { get; } + + /// + /// Standard port for this server to run at. A different one may be selected if it's busy. + /// + public ushort PortHint { get; } + + /// + /// Construct an address for local server connections. + /// + /// The server's process must match the process name, if provided here. + /// Otherwise, the port is used directly, if non-zero. The port is also preferred + /// if there are multiple ports associated with the given process name. + /// + public KnownServer(ushort portHint, string processName) + { + if (portHint == 0 && processName == null) + { + throw new ArgumentException($"One of process or port must be provided in KnownServer"); + } + PortHint = portHint; + ProcessName = processName; + } + + /// + public override string ToString() => $"KnownServer[PortHint={PortHint},ProcessName={ProcessName}]"; + + /// + /// Try to find a running server using heuristic info. + /// + /// This does a lookup of system TCP state and will throw an exception if that fails. + /// + internal bool FindServer(out int realPort) + { + realPort = 0; + + // Make sure the server is at least running. This may throw an exception. + MIB_TCPROW_OWNER_PID[] rows = GetAllTcpConnections(); + MIB_TCP6ROW_OWNER_PID[] rows6 = GetAllTcpConnections(); + + // If process is given, always try to use it, and fail if not present + if (ProcessName != null) + { + Process[] processes = Process.GetProcessesByName(ProcessName); + if (processes.Length == 0) + { + return false; + } + HashSet processIds = new HashSet(processes.Select(p => p.Id)); + List matchingPorts = new List(); + foreach (MIB_TCPROW_OWNER_PID row in rows) + { + if (processIds.Contains(row.owningPid) && row.state == MIB_TCP_STATE.MIB_TCP_STATE_LISTEN) + { + matchingPorts.Add(ConvertPort(row.localPort)); + } + // if (row.state == MIB_TCP_STATE.MIB_TCP_STATE_LISTEN) Console.WriteLine($"local {row.localPort} remote {row.remotePort} pid {row.owningPid}"); + } + foreach (MIB_TCP6ROW_OWNER_PID row in rows6) + { + if (processIds.Contains(row.owningPid) && row.state == MIB_TCP_STATE.MIB_TCP_STATE_LISTEN) + { + matchingPorts.Add(ConvertPort(row.localPort)); + } + // if (row.state == MIB_TCP_STATE.MIB_TCP_STATE_LISTEN) Console.WriteLine($"local6 {row.localPort} remote {row.remotePort} pid {row.owningPid}"); + } + if (matchingPorts.Count > 0) + { + // Prefer PortHint if given + realPort = PortHint > 0 && matchingPorts.Contains(PortHint) ? PortHint : matchingPorts[0]; + return true; + } + return false; + } + + // Use port if process is not given. This will fail later on if there's not a gRPC service there. + int netPort = ConvertPort(PortHint); + foreach (MIB_TCPROW_OWNER_PID row in rows) + { + if (row.localPort == netPort && row.state == MIB_TCP_STATE.MIB_TCP_STATE_LISTEN) + { + realPort = PortHint; + return true; + } + } + foreach (MIB_TCP6ROW_OWNER_PID row in rows6) + { + if (row.localPort == netPort && row.state == MIB_TCP_STATE.MIB_TCP_STATE_LISTEN) + { + realPort = PortHint; + return true; + } + } + return false; + } + + /// + /// Checks if the hinted port is in use by a server, as a heuristic for choosing a different port if it is. + /// + internal bool IsPortInUse() + { + if (PortHint == 0) + { + return false; + } + MIB_TCPROW_OWNER_PID[] rows = GetAllTcpConnections(); + MIB_TCP6ROW_OWNER_PID[] rows6 = GetAllTcpConnections(); + int netPort = ConvertPort(PortHint); + return rows.Any(row => netPort == row.localPort && row.state == MIB_TCP_STATE.MIB_TCP_STATE_LISTEN) + || rows6.Any(row => netPort == row.localPort && row.state == MIB_TCP_STATE.MIB_TCP_STATE_LISTEN); + } + + // Windows API + // https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getextendedtcptable + // C# marshalling example code + // http://www.pinvoke.net/default.aspx/iphlpapi/GetExtendedTcpTable.html + // https://stackoverflow.com/questions/577433/which-pid-listens-on-a-given-port-in-c-sharp + + private static ushort ConvertPort(ushort port) => (ushort)(port >> 8 | ((port & 0xFF) << 8)); + + private enum MIB_TCP_STATE : int + { + MIB_TCP_STATE_CLOSED = 1, + MIB_TCP_STATE_LISTEN = 2, + MIB_TCP_STATE_SYN_SENT = 3, + MIB_TCP_STATE_SYN_RCVD = 4, + MIB_TCP_STATE_ESTAB = 5, + MIB_TCP_STATE_FIN_WAIT1 = 6, + MIB_TCP_STATE_FIN_WAIT2 = 7, + MIB_TCP_STATE_CLOSE_WAIT = 8, + MIB_TCP_STATE_CLOSING = 9, + MIB_TCP_STATE_LAST_ACK = 10, + MIB_TCP_STATE_TIME_WAIT = 11, + MIB_TCP_STATE_DELETE_TCB = 12, + } + + private enum TCP_TABLE_CLASS : int + { + TCP_TABLE_BASIC_LISTENER, + TCP_TABLE_BASIC_CONNECTIONS, + TCP_TABLE_BASIC_ALL, + TCP_TABLE_OWNER_PID_LISTENER, + TCP_TABLE_OWNER_PID_CONNECTIONS, + TCP_TABLE_OWNER_PID_ALL, + TCP_TABLE_OWNER_MODULE_LISTENER, + TCP_TABLE_OWNER_MODULE_CONNECTIONS, + TCP_TABLE_OWNER_MODULE_ALL + } + + [StructLayout(LayoutKind.Sequential)] + private struct MIB_TCPROW_OWNER_PID + { + public MIB_TCP_STATE state; + public uint localAddr; + // Uses network byte order, but only within two bytes. Use ConvertPort to convert. + public ushort localPort; + public uint remoteAddr; + public ushort remotePort; + public int owningPid; + } + + [StructLayout(LayoutKind.Sequential)] + private struct MIB_TCP6ROW_OWNER_PID + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] localAddr; + public uint localScopeId; + public ushort localPort; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] remoteAddr; + public uint remoteScopeId; + public ushort remotePort; + public MIB_TCP_STATE state; + public int owningPid; + } + + [StructLayout(LayoutKind.Sequential)] + private struct COMMON_MIB_TCPTABLE_OWNER_PID + { + public uint numEntries; + // This is followed by a variable length array of either row type. + // It's easiest to walk through it manually in code. + } + + [DllImport("iphlpapi.dll", SetLastError = true)] + static extern uint GetExtendedTcpTable( + IntPtr tcpTable, + ref int tcpTableLength, + bool sort, + int ipVersion, + TCP_TABLE_CLASS tcpTableType, + int reserved = 0); + + private static TRow[] GetAllTcpConnections() + { + TRow[] rows; + // 2 is IPv4, 23 is IPv6 + int ipVersion; + if (typeof(TRow) == typeof(MIB_TCPROW_OWNER_PID)) + { + // IPv4 + ipVersion = 2; + } + else if (typeof(TRow) == typeof(MIB_TCP6ROW_OWNER_PID)) + { + // IPv6 + ipVersion = 23; + } + else + { + throw new Exception($"Internal error: unsupported GetExtendedTcpTable type {typeof(TRow).FullName}"); + } + int buffSize = 0; + + uint ret = GetExtendedTcpTable( + IntPtr.Zero, + ref buffSize, + true, + ipVersion, + TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL); + // 122 is "insufficient buffer", expected + if (ret != 0 && ret != 122) + { + throw new Exception("GetExtendedTcpTable in iphlpapi.dll failed with error code " + ret); + } + + // Race conditions may be possible, if this changes between allocation and re-allocation + IntPtr buffTable = Marshal.AllocHGlobal(buffSize); + try + { + ret = GetExtendedTcpTable( + buffTable, + ref buffSize, + true, + ipVersion, + TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL); + if (ret != 0) + { + throw new Exception("GetExtendedTcpTable in iphlpapi.dll failed with error code " + ret); + } + + // Get the total size and copy all row structs individually + // This is pointing to memory we've allocated, so unboxing it should be safe. +#pragma warning disable CS8605 + COMMON_MIB_TCPTABLE_OWNER_PID tab = + (COMMON_MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(COMMON_MIB_TCPTABLE_OWNER_PID)); + IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf()); + + rows = new TRow[tab.numEntries]; + for (int i = 0; i < tab.numEntries; i++) + { + rows[i] = (TRow)Marshal.PtrToStructure(rowPtr, typeof(TRow)); + rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf()); + } +#pragma warning restore CS8605 + } + finally + { + Marshal.FreeHGlobal(buffTable); + } + return rows; + } + } +} diff --git a/SoapstoneLib/SoapstoneLib/PropertySearch.cs b/SoapstoneLib/SoapstoneLib/PropertySearch.cs new file mode 100644 index 0000000..a38d39c --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/PropertySearch.cs @@ -0,0 +1,333 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using SoapstoneLib.Proto; + +namespace SoapstoneLib +{ + /// + /// Represents a search query to execute against SoulsObjects. + /// + /// At present, the representation for this query is limited to conjunctive normal form (CNF), + /// meaning it passes if all clauses succeed, where each clause can succeed if any individual + /// property checks succeed. + /// + /// In C-like syntax, this looks like: Map == "m10_00_00_00" && (EntityID == 450 || EntityID == 451) + /// This would be created like: PropertySearch.AllOf(mapCond, PropertySearch.AnyOf(entityCond, entityCond2)) + /// + /// The representation may change in the future to support arbitrary nested queries. Editors may + /// look for top-level keys to do high-level filtering (e.g. map names for maps, param names for parmas), + /// so keeping those keys in their own distinct clauses may lead to significantly better performance. + /// + public sealed class PropertySearch + { + internal PropertySearch(List> AllOfAnyConditions) + { + this.AllOfAnyConditions = AllOfAnyConditions; + } + + /// + /// Creates a PropertySearch only checking a single condition. + /// + public static PropertySearch Of(Condition cond) + { + return new PropertySearch(new List> { new List { cond } }); + } + + /// + /// Creates a PropertySearch requiring all of a given set of conditions. + /// + /// The obejcts can be a mix of Conditions and/or PropertySearches. + /// + /// Order matters for efficiency, because property checks can stop when + /// any conjunctive clause evaluates to false. + /// + /// Object is not Condition or PropertySearch + public static PropertySearch AllOf(params object[] objs) + { + List> all = new List>(); + // Each entry is assumed to be a single any-of clause + foreach (object obj in objs) + { + if (obj is Condition cond) + { + all.Add(new List { cond }); + } + else if (obj is PropertySearch search) + { + // Normally there should be one entry here, but it should be fine to AddRange to + // intersect the conditions, if for some reason there's an AllOf AllOf. + all.AddRange(search.AllOfAnyConditions); + } + else + { + throw new ArgumentException($"Unknown AllOf argument type {obj?.GetType()}, expected Condition or PropertySearch"); + } + } + return new PropertySearch(all); + } + + /// + /// Creates a PropertySearch which passes when any of given conditions are met. + /// + /// This can be chained into AllOf. + /// + /// Order matters for efficiency, because property checks can stop when + /// any disjunctive clause evaluates to true. + /// + /// No Conditions are given + public static PropertySearch AnyOf(params Condition[] conds) + { + if (conds.Length == 0) + { + throw new ArgumentException($"Given no conditions"); + } + return new PropertySearch(new List> { conds.ToList() }); + } + + /// + /// IEnumerable version of AnyOf. + /// + public static PropertySearch AnyOf(IEnumerable conds) + { + List condList = conds.ToList(); + if (condList.Count == 0) + { + throw new ArgumentException($"Given no conditions"); + } + return new PropertySearch(new List> { condList }); + } + + /// + /// All conditions in conjunctive normal form. + /// + /// This representation may change in the future. + /// + public List> AllOfAnyConditions { get; set; } + + /// + /// Returns the first condition in the search, or none if it's empty. + /// + public Condition FirstCondition => AllOfAnyConditions?.FirstOrDefault()?.FirstOrDefault(); + + /// + /// Returns whether the search succeeds for the given property accessor. + /// + /// The accessor function takes a property key and returns a value. If it returns null, + /// the condition evaluates to false. If it returns an IEnumerable of values, the condition + /// passes when any of the value passes. + /// + public bool IsMatch(Func accessor) + { + // For now, each value is accessed every time it appears. + // It would be straightforward to cache this if a value appears multiple times, + // but for now just do the simple thing. + foreach (List anyConds in AllOfAnyConditions) + { + if (!anyConds.Any(cond => cond.IsMatch(accessor.Invoke(cond.Key)))) + { + return false; + } + } + return true; + } + + /// + /// Returns all distinct property keys in the search query. + /// + public IEnumerable SearchKeys => AllOfAnyConditions.SelectMany(any => any.Select(cond => cond.Key)).Distinct(); + + /// + /// Returns whether the given property key is used in this search. + /// + public bool IsFiltered(string key) => AllOfAnyConditions.Any(any => any.Any(cond => cond.Key == key)); + + /// + /// Returns a special search predicate which is limited to the given property key. + /// + /// If the predicate returns true for a value of the given key, the full search may + /// or may not succeed. If the predicate returns false, the full search will definitely + /// not succeed. + /// + /// This can be used to pre-check a given high-level property, before evaluating more + /// expensive ones, especially if this allows skipping expensive traversals. The predicate + /// always returns true if the key is not part of the search query. + /// + public Predicate GetKeyFilter(string key) + { + List> filteredConds = new List>(); + foreach (List anyConds in AllOfAnyConditions) + { + if (anyConds.Any(cond => cond.Key == key)) + { + if (anyConds.All(cond => cond.Key == key)) + { + filteredConds.Add(anyConds); + } + else + { + // If mixed conditions between key and non-key, key no longer uniquely filters + filteredConds = null; + break; + } + } + } + return test => + { + if (filteredConds == null) + { + return true; + } + foreach (List anyConds in filteredConds) + { + if (!anyConds.Any(cond => cond.IsMatch(test))) + { + return false; + } + } + return true; + }; + } + + /// + /// Represents a condition which can be checked against a specific property. + /// + /// For instance, EntityID > 0, ModelName ~= "^c", FMG == "NpcName". + /// + public sealed class Condition + { + /// + /// Creates a Condition with the given fields. + /// + /// If it is a regex search, the Value may be compiled into a C# Regex. + /// + public Condition(PropertyComparisonType Type, string Key, object Value) + { + this.Type = Type; + this.Key = Key; + // Optimization: Regexes get precomputed. Not sure if compilation is worth it (should benchmark). + if ((Type == PropertyComparisonType.Matches || Type == PropertyComparisonType.NotMatches) + && Value is string reStr) + { + Value = new Regex(reStr, RegexOptions.Compiled); + } + this.Value = Value; + } + + /// + /// The comparison to perform between the value and IsMatch input. + /// + public PropertyComparisonType Type { get; set; } + + /// + /// The property key to access. + /// + public string Key { get; set; } + + /// + /// The value to compare the property against. + /// + /// For non-commutative comparisons, this is the left-hand side. + /// + public object Value { get; set; } + + /// + /// Tests the given value on this condition. + /// + public bool IsMatch(object test) + { + // Match any of a list of scalars + if (test is not string && test is System.Collections.IEnumerable multi) + { + foreach (object val in multi) + { + if (IsMatchScalar(val)) + { + return true; + } + } + return false; + } + return IsMatchScalar(test); + } + + private bool IsMatchScalar(object test) + { + // null operations are not supported under any circumstances + if (test == null || Value == null) + { + return false; + } + switch (Type) + { + case PropertyComparisonType.Unspecified: + return false; + case PropertyComparisonType.Equal: + // Beware of floating point equality, there is no specific check for it here. + return Value.Equals(test); + case PropertyComparisonType.NotEqual: + return !Value.Equals(test); + case PropertyComparisonType.Greater: + return IsLessThan(test, Value, true, true); + case PropertyComparisonType.Less: + return IsLessThan(test, Value, true, false); + case PropertyComparisonType.GreaterOrEqual: + return IsLessThan(test, Value, false, true); + case PropertyComparisonType.LessOrEqual: + return IsLessThan(test, Value, false, false); + case PropertyComparisonType.Matches: + return IsRegexMatch(test, Value); + case PropertyComparisonType.NotMatches: + return !IsRegexMatch(test, Value); + default: + return false; + } + } + + private static bool IsRegexMatch(object test, object value) + { + if (value is Regex re) + { + return re.IsMatch(test.ToString()); + } + // It is a bit weird to treat non-strings like regex, but it is what was requested + return Regex.IsMatch(test.ToString(), value.ToString()); + } + + private static bool IsLessThan(object test, object value, bool strict, bool invert) + { + int result; + if (test.GetType() == value.GetType() && test is IComparable testCmp) + { + result = testCmp.CompareTo(value); + } + else if (test is float or double && value is float or double) + { + result = Convert.ToDouble(test).CompareTo(Convert.ToDouble(value)); + } + else if (test is IConvertible testConv && value is IConvertible valueConv) + { + // Unknown, assume integer types; use decimal for massive precision, at the cost of performance. + try + { + result = testConv.ToDecimal(null).CompareTo(valueConv.ToDecimal(null)); + } + catch (Exception) + { + // This is incredibly expensive, especially with hundreds or thousands of comparisons. + // Unfortunately, there is no TryConvert in C# standard library. + return false; + } + } + else + { + return false; + } + // This logic is a bit tricky. Negating the result also changes strictness, + // so account for it during the strictness check itself. + return (strict != invert ? result < 0 : result <= 0) != invert; + } + } + } +} diff --git a/SoapstoneLib/SoapstoneLib/RequestedProperties.cs b/SoapstoneLib/SoapstoneLib/RequestedProperties.cs new file mode 100644 index 0000000..7e4d0ef --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/RequestedProperties.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using SoapstoneLib.Proto; + +namespace SoapstoneLib +{ + /// + /// A list of requested property keys for a search or get request. + /// + public sealed class RequestedProperties + { + /// + /// The properties, using the protobuf RequestedProperty class. + /// + public IList Properties { get; set; } + + /// + /// Adds the given property keys as properties to always fetch when present. + /// + public RequestedProperties Add(params string[] names) + { + if (Properties == null) + { + Properties = new List(); + } + foreach (string name in names) + { + Properties.Add(new RequestedProperty { Key = name }); + } + return this; + } + + /// + /// Adds the given property keys as properties to fetch when the values are non-trivial (see IsNonTrivialValue). + /// + /// This may help performance in cases where a given property key is expected to be missing + /// most of the time, or where the property is for a fixed-size array whose entries are 0 or + /// -1 if unspecified. + /// + public RequestedProperties AddNonTrivial(params string[] names) + { + if (Properties == null) + { + Properties = new List(); + } + foreach (string name in names) + { + Properties.Add(new RequestedProperty { Key = name, NonTrivialOnly = true }); + } + return this; + } + + /// + /// Returns whether a given value is non-trivial for the purpose of filtering results. + /// + /// This checks that integer-like values are positive and strings are non-empty. Floating + /// point values are currently never considered trivial. + /// + public static bool IsNonTrivialValue(object obj) + { + switch (obj) + { + case sbyte s8Value: + return s8Value > 0; + case byte u8Value: + return u8Value > 0; + case short s16Value: + return s16Value > 0; + case ushort u16Value: + return u16Value > 0; + case int s32Value: + return s32Value > 0; + case uint u32Value: + return u32Value > 0; + case long s64Value: + return s64Value > 0; + case ulong u64Value: + return u64Value > 0; + case string stringValue: + return !string.IsNullOrEmpty(stringValue); + } + return true; + } + } +} diff --git a/SoapstoneLib/SoapstoneLib/SoapstoneClient.cs b/SoapstoneLib/SoapstoneLib/SoapstoneClient.cs new file mode 100644 index 0000000..2b87a8b --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/SoapstoneClient.cs @@ -0,0 +1,317 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Net.Client; +using SoapstoneLib.Proto; +using SoapstoneLib.Proto.Internal; + +namespace SoapstoneLib +{ + /// + /// Client API for a Soapstone server instance. Clients are initialized through SoapstoneClient.Provider objects. + /// + /// This makes RPCs to the server. If the server program closes, RPC methods may throw an RpcException with an + /// Unavailable status. This can be guarded against by using Provider.TryGetClient before calling the client, + /// but it is not fully avoidable if the server becomes unavailable immediately after that. + /// + /// Any server exceptions will result in client RpcExceptions with the appropriate status and error message. + /// + public sealed class SoapstoneClient + { + private readonly TimeSpan? deadline; + private readonly GrpcChannel channel; + private readonly Soapstone.SoapstoneClient client; + + /// + /// Create a SoapstoneClient provider, which can be used to dynamically get a functioning client. + /// + /// This is required for all clients to use at the moment. Any time you need to call a server, first + /// get the client from a provider, which will return the same client as before if it's still available. + /// This is meant to handle server editors being opened and closed, as well as configuring settings + /// in client editors. + /// + public static Provider GetProvider(KnownServer server = null) + { + return new Provider(server); + } + + internal SoapstoneClient(string address, TimeSpan? deadline) + { + // This is needed for .NET Core 3 but not .NET >5. Use in case backporting is needed. + AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); + SocketsHttpHandler handler = new SocketsHttpHandler + { + // This is the only default value set if a handler is not specified. + // It comes from gRPC-internal HttpHandlerFactory + EnableMultipleHttp2Connections = true, + // The main thing to configure here: be more aggressive about checking if the server + // is up, since we'd prefer to mark clients unavailable on the faster side. + KeepAlivePingDelay = TimeSpan.FromSeconds(5), + KeepAlivePingTimeout = TimeSpan.FromSeconds(5), + }; + channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions { HttpHandler = handler }); + client = new Soapstone.SoapstoneClient(channel); + this.deadline = deadline; + } + + /// + /// Returns basic info about the editor's current state. All servers must implement this. + /// Editor resources are parts of an editor which can be individually loaded or unloaded. + /// Once a resource is loaded, various objects and functionality can be straightforwardly accessed within it. + /// + public async Task GetServerInfo() + { + ServerInfoRequest request = new ServerInfoRequest(); + return await client.GetServerInfoAsync(request); + } + + /// + /// Get objects matching a search query, with requested properties. + /// A property does not have to be requested to search against it. + /// + public async Task> SearchObjects( + EditorResource resource, + SoulsKeyType resultType, + PropertySearch search, + RequestedProperties properties = null, + SearchOptions options = null) + { + SearchObjectsRequest request = new SearchObjectsRequest + { + Resource = resource, + ResultType = InternalConversions.ToPrimaryKeyType(resultType), + Search = InternalConversions.ToPropertySearch(search), + Properties = { properties?.Properties ?? new List() }, + Options = options, + }; + SearchObjectsResponse response = await client.SearchObjectsAsync(request, MakeOptions()); + return response.Results.Select(InternalConversions.FromGameObject).ToList(); + } + + /// + /// Returns a single object, within an editor resource type. + /// This can be used if the exact key is known, or to get more properties after a broader search. + /// It returns null if object is not found (as opposed to an error). + /// + public async Task GetObject( + EditorResource resource, + SoulsKey key, + RequestedProperties properties = null) + { + GetObjectRequest request = new GetObjectRequest + { + Resource = resource, + Key = InternalConversions.ToPrimaryKey(key), + Properties = { properties?.Properties ?? new List() }, + }; + GetObjectResponse response = await client.GetObjectAsync(request, MakeOptions()); + // At present, this may be empty, as the client may know the exact key and request it + return response.Result == null ? null : InternalConversions.FromGameObject(response.Result); + } + + /// + /// Returns objects in batch, within an editor resource type. + /// It returns only the objects which could be found. + /// + public async Task> GetObjects( + EditorResource resource, + IEnumerable keys, + RequestedProperties properties = null) + { + BatchGetObjectsRequest request = new BatchGetObjectsRequest + { + Resource = resource, + Keys = { keys.Select(InternalConversions.ToPrimaryKey) }, + Properties = { properties?.Properties ?? new List() }, + }; + BatchGetObjectsResponse response = await client.BatchGetObjectsAsync(request, MakeOptions()); + return response.Results.Select(InternalConversions.FromGameObject).ToList(); + } + + /// + /// Open a resource, like a map by name. + /// + public async Task OpenResource(EditorResource resource) + { + OpenResourceRequest request = new OpenResourceRequest + { + Resource = resource, + }; + await client.OpenResourceAsync(request, MakeOptions()); + } + + /// + /// Jump to or frame the given object within the editor. + /// + public async Task OpenObject(EditorResource resource, SoulsKey key) + { + OpenObjectRequest request = new OpenObjectRequest + { + Resource = resource, + Key = InternalConversions.ToPrimaryKey(key), + }; + await client.OpenObjectAsync(request, MakeOptions()); + } + + /// + /// Start a given search in the editor. + /// + public async Task OpenSearch( + EditorResource resource, + SoulsKeyType resultType, + PropertySearch search, + bool openFirstResult) + { + OpenSearchRequest request = new OpenSearchRequest + { + Resource = resource, + ResultType = InternalConversions.ToPrimaryKeyType(resultType), + Search = InternalConversions.ToPropertySearch(search), + OpenFirstResult = openFirstResult, + }; + await client.OpenSearchAsync(request, MakeOptions()); + } + + private CallOptions MakeOptions() + { + DateTime? deadlineTime = null; + if (deadline is TimeSpan span) + { + deadlineTime = DateTime.UtcNow.Add(span); + } + return new CallOptions(deadline: deadlineTime); + } + + internal Task ShutdownAsync() => channel.ShutdownAsync(); + + internal bool IsAvailable() + { + // At least for local connections, it appears as though the channel is immediately in Ready state, + // and stays that way. Connecting state is used after a disconnection occurs, with random backoff. + // This will need to be revisited if Connecting can occur at the start, since the result of this + // is used to determine if a new connection may be needed. + if (channel.State == ConnectivityState.Ready) + { + // We could also add additional checking here if requested, like a manual health check. + return true; + } + return false; + } + + /// + /// Client initializer which is tolerant of servers going up/down and ports changing. + /// + /// To get a client, call TryGetClient every time a client is required. If the server is + /// detected as running, it will output a client, potentially changing server address and + /// client configuration as needed. + /// + public sealed class Provider + { + private KnownServer targetServer; + private TimeSpan? targetDeadline; + private SoapstoneClient lastClient; + private bool findServer; + private int lastClientPort; + + internal Provider(KnownServer server = null) + { + targetServer = server; + // Default deadline. Should be enough for almost all cases, unless server hangs. + targetDeadline = TimeSpan.FromSeconds(30); + } + + /// + /// The server to connect to the next time TryGetClient is called. + /// + /// If this is set to null, no connection will be attempted. + /// + public KnownServer Server + { + get => targetServer; + set + { + findServer = true; + targetServer = value; + } + } + + /// + /// The deadline for all RPCs from the client side. By default, this is 30 seconds. + /// Per-RPC overrides are currently not supported. + /// + /// This means that after the client has been making an RPC for this length of the time, + /// it will return early with a DeadlineExceeded status. The server call may also end + /// early, but only if it checks its CancellationToken. + /// + /// If this is set to null, no deadline will be enforced. + /// + public TimeSpan? Deadline + { + get => targetDeadline; + set + { + findServer = true; + targetDeadline = value; + } + } + + /// + /// For informational purposes, the remote localhost port a server was last detected at + /// when TryGetClient was last called, if the RPC channel still appears to be active. + /// + public int? LastPort + { + get + { + if (lastClient != null && lastClient.IsAvailable() && lastClientPort > 0) + { + return lastClientPort; + } + return null; + } + } + + /// + /// Outputs a client instance for accessing the Server spec. + /// + /// If this returns false, no server could be found running on the given process/port. + /// + public bool TryGetClient(out SoapstoneClient client) + { + client = null; + if (targetServer == null) + { + return false; + } + if (!findServer && lastClient != null && lastClient.IsAvailable()) + { + client = lastClient; + return true; + } + if (!targetServer.FindServer(out int actualPort)) + { + return false; + } + if (actualPort == lastClientPort && lastClient != null && lastClient.IsAvailable()) + { + findServer = false; + client = lastClient; + return true; + } + if (lastClient != null) + { + // Clean up in background + lastClient.ShutdownAsync(); + } + lastClientPort = actualPort; + lastClient = new SoapstoneClient("http://localhost:" + actualPort, targetDeadline); + findServer = false; + client = lastClient; + return true; + } + } + } +} \ No newline at end of file diff --git a/SoapstoneLib/SoapstoneLib/SoapstoneLib.csproj b/SoapstoneLib/SoapstoneLib/SoapstoneLib.csproj new file mode 100644 index 0000000..6a7d325 --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/SoapstoneLib.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + True + + + + + + + + + + diff --git a/SoapstoneLib/SoapstoneLib/SoapstoneServer.cs b/SoapstoneLib/SoapstoneLib/SoapstoneServer.cs new file mode 100644 index 0000000..c0722da --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/SoapstoneServer.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Grpc.Core; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using SoapstoneLib.Proto; +using SoapstoneLib.Proto.Internal; + +namespace SoapstoneLib +{ + /// + /// Runs a Soapstone server given a service implementation. + /// + /// Full server exception stack traces will visible to clients when RpcException is not used. + /// + public static class SoapstoneServer + { + private static readonly object initLock = new object(); + private static volatile SoapstoneServiceV1 service; + private static volatile IHost host; + + /// + /// Initializes a Kestrel server with the given Soapstone service instance. + /// + /// The server runs on the KnownServer port, if it is available, and a random port otherwise. + /// + /// This can only be done once. + /// + public static async Task RunAsync(KnownServer server, SoapstoneServiceV1 newService, string[] args = null) + { + if (newService == null) + { + throw new ArgumentNullException(nameof(newService)); + } + // By default, ASP.NET gRPC is static. + // https://github.com/grpc/grpc-dotnet/issues/1628 has some potential pointers to alternatives. + lock (initLock) + { + if (service != null) + { + throw new InvalidOperationException($"SoapstoneService already initialized with {service}"); + } + service = newService; + } + int port = server.PortHint; + if (server.IsPortInUse()) + { + port = 0; + } + host = CreateHostBuilder(port, args ?? Array.Empty()).Build(); + await host.RunAsync(); + } + + /// + /// Determines the port the Kestrel server is currently running at. + /// + public static int? GetRunningPort() + { + if (host == null) + { + return null; + } + try + { + // https://stackoverflow.com/questions/71865319/net-core-6-0-get-kestrels-dynamically-bound-port-port-0 + IServer server = host.Services.GetRequiredService(); + IServerAddressesFeature addressFeature = server.Features.Get(); + if (addressFeature.Addresses.Count > 0) + { + return new Uri(addressFeature.Addresses.First()).Port; + } + } + catch (Exception) + { + } + return null; + } + + private static SoapstoneServiceV1 GetService() + { + if (service == null) + { + throw new RpcException(new Status(StatusCode.Internal, "SoapstoneService initialized in an invalid state")); + } + return service; + } + + private sealed class SoapstoneImpl : Soapstone.SoapstoneBase + { + public override async Task GetServerInfo(ServerInfoRequest request, ServerCallContext context) + { + // Note: context.Peer is the caller port. context.Host is this port + SoapstoneServiceV1 service = GetService(); + return await service.GetServerInfo(context); + } + + public override async Task SearchObjects(SearchObjectsRequest request, ServerCallContext context) + { + SoapstoneServiceV1 service = GetService(); + EditorResource resource = CheckResource(request.Resource, "resource"); + PropertySearch search = InternalConversions.FromPropertySearch(CheckNonNull(request.Search, "search")); + RequestedProperties properties = new RequestedProperties { Properties = request.Properties }; + SearchOptions options = request.Options ?? new SearchOptions(); + IEnumerable result = new List(); + if (InternalConversions.FromPrimaryKeyType(CheckNonNull(request.ResultType, "resultType"), out SoulsKeyType resultType)) + { + result = await service.SearchObjects(context, resource, resultType, search, properties, options); + } + return new SearchObjectsResponse { Results = { result.Select(InternalConversions.ToGameObject) } }; + } + + public override async Task GetObject(GetObjectRequest request, ServerCallContext context) + { + SoapstoneServiceV1 service = GetService(); + EditorResource resource = CheckResource(request.Resource, "resource"); + SoulsKey key = InternalConversions.FromPrimaryKey(CheckNonNull(request.Key, "key")); + RequestedProperties properties = new RequestedProperties { Properties = request.Properties }; + IEnumerable result = await service.GetObjects(context, resource, new List { key }, properties); + SoulsObject first = result.First(); + // Could also throw NotFound here, but keep things nullable for now. + // Would just require a client/server update if changed. + return new GetObjectResponse { Result = first == null ? null : InternalConversions.ToGameObject(first) }; + } + + public override async Task BatchGetObjects(BatchGetObjectsRequest request, ServerCallContext context) + { + SoapstoneServiceV1 service = GetService(); + EditorResource resource = CheckResource(request.Resource, "resource"); + List keys = request.Keys.Select(InternalConversions.FromPrimaryKey).ToList(); + RequestedProperties properties = new RequestedProperties { Properties = request.Properties }; + IEnumerable result = await service.GetObjects(context, resource, keys, properties); + return new BatchGetObjectsResponse { Results = { result.Select(InternalConversions.ToGameObject) } }; + } + + public override async Task OpenResource(OpenResourceRequest request, ServerCallContext context) + { + SoapstoneServiceV1 service = GetService(); + EditorResource resource = CheckResource(request.Resource, "resource"); + await service.OpenResource(context, resource); + return new OpenResourceResponse(); + } + + public override async Task OpenObject(OpenObjectRequest request, ServerCallContext context) + { + SoapstoneServiceV1 service = GetService(); + EditorResource resource = CheckResource(request.Resource, "resource"); + SoulsKey key = InternalConversions.FromPrimaryKey(CheckNonNull(request.Key, "key")); + await service.OpenObject(context, resource, key); + return new OpenObjectResponse(); + } + + public override async Task OpenSearch(OpenSearchRequest request, ServerCallContext context) + { + SoapstoneServiceV1 service = GetService(); + EditorResource resource = CheckResource(request.Resource, "resource"); + PropertySearch search = InternalConversions.FromPropertySearch(CheckNonNull(request.Search, "search")); + if (InternalConversions.FromPrimaryKeyType(CheckNonNull(request.ResultType, "resultType"), out SoulsKeyType resultType)) + { + await service.OpenSearch(context, resource, resultType, search, request.OpenFirstResult); + } + return new OpenSearchResponse(); + } + + private static T CheckNonNull(T val, string name) + { + if (val == null) + { + throw new ArgumentNullException(name); + } + return val; + } + + private static EditorResource CheckResource(EditorResource val, string name) + { + if (val == null) + { + throw new ArgumentNullException(name); + } + if (val.Type == EditorResourceType.Unspecified) + { + throw new ArgumentException($"EditorResource argument must define a type: {val}"); + } + if (val.Game == FromSoftGame.Unspecified) + { + throw new ArgumentException($"EditorResource argument must define a game: {val}"); + } + return val; + } + } + + // Default ASP.NET gRPC setup, as of September 2022. + // TODO: Should this switch to use WebApplication? + private static IHostBuilder CreateHostBuilder(int port, string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder + .UseKestrel() + .UseStartup() + .ConfigureKestrel(serverOptions => + { + serverOptions.ConfigureEndpointDefaults(listenOptions => + { + // Otherwise, with default value (1 and 2), results in "Error starting gRPC call. + // HttpRequestException: An HTTP/2 connection could not be established because + // the server did not complete the HTTP/2 handshake." + listenOptions.Protocols = HttpProtocols.Http2; + }); + }) + .UseUrls($"http://127.0.0.1:{port}"); + }); + } + + private sealed class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddHostFiltering(options => + { + options.AllowedHosts = new[] { "localhost", "127.0.0.1", "[::1]" }; + }); + // This option returns exception messages, but unfortunately stack traces will require interceptors. + // Something like https://stackoverflow.com/questions/52078579/global-exception-handling-in-grpc-c-sharp + services.AddGrpc(options => options.EnableDetailedErrors = true); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapGrpcService(); + }); + } + } + } +} diff --git a/SoapstoneLib/SoapstoneLib/SoapstoneServiceV1.cs b/SoapstoneLib/SoapstoneLib/SoapstoneServiceV1.cs new file mode 100644 index 0000000..08911fc --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/SoapstoneServiceV1.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Grpc.Core; +using SoapstoneLib.Proto; + +namespace SoapstoneLib +{ + /// + /// Service class to be implemented by servers. + /// + /// This can throw RpcException to return specific error statuses. Otherwise, other exceptions + /// are transformed into Internal error statuses, alongside the full server stack trace. + /// + public abstract class SoapstoneServiceV1 + { + /// + /// Returns basic info about the editor's current state. All servers must implement this. + /// Editor resources are parts of an editor which can be individually loaded or unloaded. + /// Once a resource is loaded, various objects and functionality can be straightforwardly accessed within it. + /// + public abstract Task GetServerInfo(ServerCallContext context); + + /// + /// Get objects matching a search query, with requested properties. + /// A property does not have to be requested to search against it. + /// + public virtual Task> SearchObjects( + ServerCallContext context, + EditorResource resource, + SoulsKeyType resultType, + PropertySearch search, + RequestedProperties properties, + SearchOptions options) + { + throw new RpcException(new Status(StatusCode.Unimplemented, "Not supported in server instance")); + } + + /// + /// Returns game objects, within an editor resource type. + /// This can be used if the exact key is known, or to get more properties after a broader search. + /// + public virtual Task> GetObjects( + ServerCallContext context, + EditorResource resource, + List keys, + RequestedProperties properties) + { + throw new RpcException(new Status(StatusCode.Unimplemented, "Not supported in server instance")); + } + + /// + /// Open a resource, like a map by name. + /// + public virtual Task OpenResource(ServerCallContext context, EditorResource resource) + { + throw new RpcException(new Status(StatusCode.Unimplemented, "Not supported in server instance")); + } + + /// + /// Jump to or frame the given object within the editor. + /// + public virtual Task OpenObject(ServerCallContext context, EditorResource resource, SoulsKey key) + { + throw new RpcException(new Status(StatusCode.Unimplemented, "Not supported in server instance")); + } + + /// + /// Start a given search in the editor. + /// + public virtual Task OpenSearch( + ServerCallContext context, + EditorResource resource, + SoulsKeyType resultType, + PropertySearch search, + bool openFirstResult) + { + throw new RpcException(new Status(StatusCode.Unimplemented, "Not supported in server instance")); + } + } +} diff --git a/SoapstoneLib/SoapstoneLib/SoulsFmg.Data.cs b/SoapstoneLib/SoapstoneLib/SoulsFmg.Data.cs new file mode 100644 index 0000000..f205c39 --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/SoulsFmg.Data.cs @@ -0,0 +1,1902 @@ +using System.Collections.Generic; +using SoapstoneLib.Proto; + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +// This file is autogenerated by TestConsoleApp. +// DO NOT MANUALLY EDIT THIS FILE. + +namespace SoapstoneLib +{ + public static partial class SoulsFmg + { + /// + /// The language categorization for any FMG in any supported game. + /// + /// This corresponds to a language folder in the game files. + /// + /// Do not rely on the integer value of this enum, as it may change over time. Only use the name. + /// + public enum FmgLanguage + { + Unspecified, + AsiaEnglish, + BrazilPortuguese, + BritishEnglish, + Danish, + Dutch, + English, + Finnish, + French, + German, + Italian, + Japanese, + Korean, + Norwegian, + Polish, + PortugalPortuguese, + Russian, + SimplifiedChinese, + SpainSpanish, + Spanish, + Swedish, + Thai, + TraditionalChinese, + Turkish, + } + + /// + /// Type specifier for any item or menu FMG in any supported game. + /// + /// Each FMG file within a game has a unique type, within any given language. + /// + /// Do not rely on the integer value of this enum, as it may change over time. Only use the name. + /// + public enum FmgType + { + Unspecified, + AccessoryCaption, + AccessoryCaption_DLC1, + AccessoryCaption_DLC2, + AccessoryCaption_Patch, + AccessoryInfo, + AccessoryInfo_DLC1, + AccessoryInfo_DLC2, + AccessoryInfo_Patch, + AccessoryName, + AccessoryName_DLC1, + AccessoryName_DLC2, + AccessoryName_Patch, + ActionButtonText, + ArtsCaption, + ArtsName, + BloodMsg, + BloodMsg_DLC1, + BloodMsg_DLC2, + BloodMsg_Patch, + BloodMsgConjunction, + BloodMsgSentence, + BloodMsgWord, + BloodMsgWordCategory, + BonfireMenu, + BonfireName, + CauseOfDeath, + CharaMaking, + CharaName, + Dialogues, + Dialogues_Patch, + DS2Common, + DS2Shop, + EventText, + EventText_DLC1, + EventText_DLC2, + EventText_Patch, + EventTextForMap, + EventTextForTalk, + FeatureCaption, + FeatureInfo, + FeatureName, + GameDialogues, + GameDialogues_DLC1, + GameDialogues_DLC2, + GameKeyGuide, + GameLineHelp, + GameLineHelp_DLC1, + GameLineHelp_DLC2, + GameMenuText, + GameMenuText_DLC1, + GameMenuText_DLC2, + GameSystemMessagePS4, + GameSystemMessagePS4_DLC1, + GameSystemMessagePS4_DLC2, + GameSystemMessageWindows, + GameSystemMessageWindows_DLC1, + GameSystemMessageWindows_DLC2, + GameSystemMessageXboxOne, + GameSystemMessageXboxOne_DLC1, + GameSystemMessageXboxOne_DLC2, + GemCaption, + GemEffect, + GemInfo, + GemName, + GemPrefix, + GoodsCaption, + GoodsCaption_DLC1, + GoodsCaption_DLC2, + GoodsCaption_Patch, + GoodsDialog, + GoodsInfo, + GoodsInfo_DLC1, + GoodsInfo_DLC2, + GoodsInfo_Patch, + GoodsInfo2, + GoodsName, + GoodsName_DLC1, + GoodsName_DLC2, + GoodsName_Patch, + IconHelp, + InGameMenu, + InGameSystem, + ItemCaption, + ItemInfo, + ItemName, + KeyGuide, + KeyGuide_Patch, + LineHelp, + LineHelp_Patch, + LoadingText, + LoadingTitle, + MagicCaption, + MagicCaption_DLC1, + MagicCaption_DLC2, + MagicCaption_Patch, + MagicInfo, + MagicInfo_DLC1, + MagicInfo_DLC2, + MagicName, + MagicName_DLC1, + MagicName_DLC2, + MagicName_Patch, + MenuContext, + MenuGeneralText, + MenuGeneralText_Patch, + MenuOther, + MenuOther_Patch, + MovieSubtitle, + NetworkMessage, + NpcMenu, + NpcName, + NpcName_DLC1, + NpcName_DLC2, + NpcName_Patch, + PlaceName, + PlaceName_DLC1, + PlaceName_DLC2, + PlaceName_Patch, + PluralSelect, + Prologue, + ProtectorCaption, + ProtectorCaption_DLC1, + ProtectorCaption_DLC2, + ProtectorCaption_Patch, + ProtectorInfo, + ProtectorInfo_Patch, + ProtectorName, + ProtectorName_DLC1, + ProtectorName_DLC2, + ProtectorName_Patch, + Skills, + StaffRoll, + SystemMessageDC, + SystemMessageWindows, + SystemMessageWindows_Patch, + SystemTagsWindows, + TalkMsg, + TalkMsg_DLC1, + TalkMsg_DLC2, + TalkMsg_Patch, + TalkMsgFemalePCAlt, + TextDisplayTagList, + TextEmbedImageNameWindows, + TitleFlow, + TitleMenu, + TosWindows, + TutorialBody, + TutorialTitle, + WeaponCaption, + WeaponCaption_DLC1, + WeaponCaption_DLC2, + WeaponCaption_Patch, + WeaponEffect, + WeaponInfo, + WeaponInfo_Patch, + WeaponName, + WeaponName_DLC1, + WeaponName_DLC2, + WeaponName_Patch, + WeaponType, + } + + internal static bool TryGetFmgGameInfo(FromSoftGame game, out FmgGameInfo info) + { + if (fmgGames == null) + { + fmgGames = MakeFmgGames(); + } + return fmgGames.TryGetValue(game, out info); + } + + // Lazily initialize this to keep things lightweight if FMG is not needed. + // Not synchronizing initialization should be fine. + private static Dictionary fmgGames; + private static Dictionary MakeFmgGames() => new() + { + [FromSoftGame.DemonsSouls] = new FmgGameInfo + { + ByType = new() + { + [FmgType.AccessoryCaption] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.AccessoryCaption, FmgType.AccessoryCaption, "アクセサリうんちく", 27), + [FmgType.AccessoryInfo] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.AccessoryInfo, FmgType.AccessoryInfo, "アクセサリ説明", 23), + [FmgType.AccessoryName] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.AccessoryName, FmgType.AccessoryName, "アクセサリ名", 13), + [FmgType.BloodMsg] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.BloodMsg, FmgType.BloodMsg, "血文字", 2), + [FmgType.Dialogues] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.Dialogues, FmgType.Dialogues, "ダイアログ", 78), + [FmgType.EventText] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.EventText, FmgType.EventText, "イベントテキスト", 30), + [FmgType.FeatureCaption] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.FeatureCaption, FmgType.FeatureCaption, "特徴うんちく", 17), + [FmgType.FeatureInfo] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.FeatureInfo, FmgType.FeatureInfo, "特徴説明", 16), + [FmgType.FeatureName] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.FeatureName, FmgType.FeatureName, "特徴名", 15), + [FmgType.GoodsCaption] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.GoodsCaption, FmgType.GoodsCaption, "アイテムうんちく", 24), + [FmgType.GoodsInfo] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.GoodsInfo, FmgType.GoodsInfo, "アイテム説明", 20), + [FmgType.GoodsName] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.GoodsName, FmgType.GoodsName, "アイテム名", 10), + [FmgType.InGameMenu] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.InGameMenu, FmgType.InGameMenu, "インゲームメニュー", 70), + [FmgType.KeyGuide] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.KeyGuide, FmgType.KeyGuide, "キーガイド", 79), + [FmgType.LineHelp] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.LineHelp, FmgType.LineHelp, "一行ヘルプ", 80), + [FmgType.MagicCaption] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.MagicCaption, FmgType.MagicCaption, "魔法うんちく", 29), + [FmgType.MagicInfo] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.MagicInfo, FmgType.MagicInfo, "魔法説明", 28), + [FmgType.MagicName] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.MagicName, FmgType.MagicName, "魔法名", 14), + [FmgType.MenuContext] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.MenuContext, FmgType.MenuContext, "項目ヘルプ", 81), + [FmgType.MenuGeneralText] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.MenuGeneralText, FmgType.MenuGeneralText, "メニュー共通テキスト", 76), + [FmgType.MenuOther] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.MenuOther, FmgType.MenuOther, "メニューその他", 77), + [FmgType.MovieSubtitle] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.MovieSubtitle, FmgType.MovieSubtitle, "ムービー字幕", 3), + [FmgType.NpcName] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.NpcName, FmgType.NpcName, "NPC名", 18), + [FmgType.PlaceName] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.PlaceName, FmgType.PlaceName, "地名", 19), + [FmgType.ProtectorCaption] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.ProtectorCaption, FmgType.ProtectorCaption, "防具うんちく", 26), + [FmgType.ProtectorInfo] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.ProtectorInfo, FmgType.ProtectorInfo, "防具説明", 22), + [FmgType.ProtectorName] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.ProtectorName, FmgType.ProtectorName, "防具名", 12), + [FmgType.TalkMsg] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.TalkMsg, FmgType.TalkMsg, "会話", 1), + [FmgType.TextDisplayTagList] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Menu, FmgType.TextDisplayTagList, FmgType.TextDisplayTagList, "テキスト表示用タグ一覧", 90), + [FmgType.WeaponCaption] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.WeaponCaption, FmgType.WeaponCaption, "武器うんちく", 25), + [FmgType.WeaponInfo] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.WeaponInfo, FmgType.WeaponInfo, "武器説明", 21), + [FmgType.WeaponName] = new FmgKeyInfo(FromSoftGame.DemonsSouls, FmgCategory.Item, FmgType.WeaponName, FmgType.WeaponName, "武器名", 11), + }, + Overrides = new() + { + + }, + ByFmgName = new() + { + ["NPC名"] = new List { FmgType.NpcName }, + ["アイテムうんちく"] = new List { FmgType.GoodsCaption }, + ["アイテム名"] = new List { FmgType.GoodsName }, + ["アイテム説明"] = new List { FmgType.GoodsInfo }, + ["アクセサリうんちく"] = new List { FmgType.AccessoryCaption }, + ["アクセサリ名"] = new List { FmgType.AccessoryName }, + ["アクセサリ説明"] = new List { FmgType.AccessoryInfo }, + ["イベントテキスト"] = new List { FmgType.EventText }, + ["インゲームメニュー"] = new List { FmgType.InGameMenu }, + ["キーガイド"] = new List { FmgType.KeyGuide }, + ["ダイアログ"] = new List { FmgType.Dialogues }, + ["テキスト表示用タグ一覧"] = new List { FmgType.TextDisplayTagList }, + ["ムービー字幕"] = new List { FmgType.MovieSubtitle }, + ["メニューその他"] = new List { FmgType.MenuOther }, + ["メニュー共通テキスト"] = new List { FmgType.MenuGeneralText }, + ["一行ヘルプ"] = new List { FmgType.LineHelp }, + ["会話"] = new List { FmgType.TalkMsg }, + ["地名"] = new List { FmgType.PlaceName }, + ["武器うんちく"] = new List { FmgType.WeaponCaption }, + ["武器名"] = new List { FmgType.WeaponName }, + ["武器説明"] = new List { FmgType.WeaponInfo }, + ["特徴うんちく"] = new List { FmgType.FeatureCaption }, + ["特徴名"] = new List { FmgType.FeatureName }, + ["特徴説明"] = new List { FmgType.FeatureInfo }, + ["血文字"] = new List { FmgType.BloodMsg }, + ["防具うんちく"] = new List { FmgType.ProtectorCaption }, + ["防具名"] = new List { FmgType.ProtectorName }, + ["防具説明"] = new List { FmgType.ProtectorInfo }, + ["項目ヘルプ"] = new List { FmgType.MenuContext }, + ["魔法うんちく"] = new List { FmgType.MagicCaption }, + ["魔法名"] = new List { FmgType.MagicName }, + ["魔法説明"] = new List { FmgType.MagicInfo }, + }, + ByBinderID = new() + { + [1] = new List { FmgType.TalkMsg }, + [2] = new List { FmgType.BloodMsg }, + [3] = new List { FmgType.MovieSubtitle }, + [10] = new List { FmgType.GoodsName }, + [11] = new List { FmgType.WeaponName }, + [12] = new List { FmgType.ProtectorName }, + [13] = new List { FmgType.AccessoryName }, + [14] = new List { FmgType.MagicName }, + [15] = new List { FmgType.FeatureName }, + [16] = new List { FmgType.FeatureInfo }, + [17] = new List { FmgType.FeatureCaption }, + [18] = new List { FmgType.NpcName }, + [19] = new List { FmgType.PlaceName }, + [20] = new List { FmgType.GoodsInfo }, + [21] = new List { FmgType.WeaponInfo }, + [22] = new List { FmgType.ProtectorInfo }, + [23] = new List { FmgType.AccessoryInfo }, + [24] = new List { FmgType.GoodsCaption }, + [25] = new List { FmgType.WeaponCaption }, + [26] = new List { FmgType.ProtectorCaption }, + [27] = new List { FmgType.AccessoryCaption }, + [28] = new List { FmgType.MagicInfo }, + [29] = new List { FmgType.MagicCaption }, + [30] = new List { FmgType.EventText }, + [70] = new List { FmgType.InGameMenu }, + [76] = new List { FmgType.MenuGeneralText }, + [77] = new List { FmgType.MenuOther }, + [78] = new List { FmgType.Dialogues }, + [79] = new List { FmgType.KeyGuide }, + [80] = new List { FmgType.LineHelp }, + [81] = new List { FmgType.MenuContext }, + [90] = new List { FmgType.TextDisplayTagList }, + }, + ToLanguageEnum = new() + { + ["asia_english"] = FmgLanguage.AsiaEnglish, + ["french"] = FmgLanguage.French, + ["german"] = FmgLanguage.German, + ["italian"] = FmgLanguage.Italian, + ["japanese"] = FmgLanguage.Japanese, + ["korean"] = FmgLanguage.Korean, + ["na_english"] = FmgLanguage.English, + ["spanish"] = FmgLanguage.SpainSpanish, + ["tchinese"] = FmgLanguage.TraditionalChinese, + ["uk_english"] = FmgLanguage.BritishEnglish, + }, + FromLanguageEnum = new() + { + [FmgLanguage.AsiaEnglish] = "asia_english", + [FmgLanguage.French] = "french", + [FmgLanguage.German] = "german", + [FmgLanguage.Italian] = "italian", + [FmgLanguage.Japanese] = "japanese", + [FmgLanguage.Korean] = "korean", + [FmgLanguage.English] = "na_english", + [FmgLanguage.SpainSpanish] = "spanish", + [FmgLanguage.TraditionalChinese] = "tchinese", + [FmgLanguage.BritishEnglish] = "uk_english", + }, + }, + [FromSoftGame.DarkSoulsPtde] = new FmgGameInfo + { + ByType = new() + { + [FmgType.AccessoryCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.AccessoryCaption, FmgType.AccessoryCaption, "アクセサリうんちく", 27), + [FmgType.AccessoryCaption_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.AccessoryCaption_Patch, FmgType.AccessoryCaption, "アクセサリうんちくパッチ", 109), + [FmgType.AccessoryInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.AccessoryInfo, FmgType.AccessoryInfo, "アクセサリ説明", 23), + [FmgType.AccessoryInfo_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.AccessoryInfo_Patch, FmgType.AccessoryInfo, "アクセサリ説明パッチ", 112), + [FmgType.AccessoryName] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.AccessoryName, FmgType.AccessoryName, "アクセサリ名", 13), + [FmgType.AccessoryName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.AccessoryName_Patch, FmgType.AccessoryName, "アクセサリ名パッチ", 113), + [FmgType.BloodMsg] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.BloodMsg, FmgType.BloodMsg, "血文字", 2), + [FmgType.BloodMsg_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.BloodMsg_Patch, FmgType.BloodMsg, "血文字パッチ", 107), + [FmgType.Dialogues] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.Dialogues, FmgType.Dialogues, "ダイアログ", 78), + [FmgType.Dialogues_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.Dialogues_Patch, FmgType.Dialogues, "ダイアログパッチ", 102), + [FmgType.EventText] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.EventText, FmgType.EventText, "イベントテキスト", 30), + [FmgType.EventText_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.EventText_Patch, FmgType.EventText, "イベントテキストパッチ", 101), + [FmgType.FeatureCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.FeatureCaption, FmgType.FeatureCaption, "特徴うんちく", 17), + [FmgType.FeatureInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.FeatureInfo, FmgType.FeatureInfo, "特徴説明", 16), + [FmgType.FeatureName] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.FeatureName, FmgType.FeatureName, "特徴名", 15), + [FmgType.GoodsCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.GoodsCaption, FmgType.GoodsCaption, "アイテムうんちく", 24), + [FmgType.GoodsCaption_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.GoodsCaption_Patch, FmgType.GoodsCaption, "アイテムうんちくパッチ", 100), + [FmgType.GoodsInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.GoodsInfo, FmgType.GoodsInfo, "アイテム説明", 20), + [FmgType.GoodsInfo_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.GoodsInfo_Patch, FmgType.GoodsInfo, "アイテム説明パッチ", 110), + [FmgType.GoodsName] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.GoodsName, FmgType.GoodsName, "アイテム名", 10), + [FmgType.GoodsName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.GoodsName_Patch, FmgType.GoodsName, "アイテム名パッチ", 111), + [FmgType.InGameMenu] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.InGameMenu, FmgType.InGameMenu, "インゲームメニュー", 70), + [FmgType.KeyGuide] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.KeyGuide, FmgType.KeyGuide, "キーガイド", 79), + [FmgType.KeyGuide_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.KeyGuide_Patch, FmgType.KeyGuide, "キーガイドパッチ", 122), + [FmgType.LineHelp] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.LineHelp, FmgType.LineHelp, "一行ヘルプ", 80), + [FmgType.LineHelp_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.LineHelp_Patch, FmgType.LineHelp, "一行ヘルプパッチ", 121), + [FmgType.MagicCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.MagicCaption, FmgType.MagicCaption, "魔法うんちく", 29), + [FmgType.MagicCaption_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.MagicCaption_Patch, FmgType.MagicCaption, "魔法うんちくパッチ", 105), + [FmgType.MagicInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.MagicInfo, FmgType.MagicInfo, "魔法説明", 28), + [FmgType.MagicName] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.MagicName, FmgType.MagicName, "魔法名", 14), + [FmgType.MagicName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.MagicName_Patch, FmgType.MagicName, "魔法名パッチ", 118), + [FmgType.MenuContext] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.MenuContext, FmgType.MenuContext, "項目ヘルプ", 81), + [FmgType.MenuGeneralText] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.MenuGeneralText, FmgType.MenuGeneralText, "メニュー共通テキスト", 76), + [FmgType.MenuGeneralText_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.MenuGeneralText_Patch, FmgType.MenuGeneralText, "メニュー共通テキストパッチ", 124), + [FmgType.MenuOther] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.MenuOther, FmgType.MenuOther, "メニューその他", 77), + [FmgType.MenuOther_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.MenuOther_Patch, FmgType.MenuOther, "メニューその他パッチ", 123), + [FmgType.MovieSubtitle] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.MovieSubtitle, FmgType.MovieSubtitle, "ムービー字幕", 3), + [FmgType.NpcName] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.NpcName, FmgType.NpcName, "NPC名", 18), + [FmgType.NpcName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.NpcName_Patch, FmgType.NpcName, "NPC名パッチ", 119), + [FmgType.PlaceName] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.PlaceName, FmgType.PlaceName, "地名", 19), + [FmgType.PlaceName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.PlaceName_Patch, FmgType.PlaceName, "地名パッチ", 120), + [FmgType.ProtectorCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.ProtectorCaption, FmgType.ProtectorCaption, "防具うんちく", 26), + [FmgType.ProtectorCaption_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.ProtectorCaption_Patch, FmgType.ProtectorCaption, "防具うんちくパッチ", 108), + [FmgType.ProtectorInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.ProtectorInfo, FmgType.ProtectorInfo, "防具説明", 22), + [FmgType.ProtectorInfo_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.ProtectorInfo_Patch, FmgType.ProtectorInfo, "防具説明パッチ", 116), + [FmgType.ProtectorName] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.ProtectorName, FmgType.ProtectorName, "防具名", 12), + [FmgType.ProtectorName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.ProtectorName_Patch, FmgType.ProtectorName, "防具名パッチ", 117), + [FmgType.SystemMessageWindows] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.SystemMessageWindows, FmgType.SystemMessageWindows, "システムメッセージ_win32", 92), + [FmgType.SystemMessageWindows_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.SystemMessageWindows_Patch, FmgType.SystemMessageWindows, "システムメッセージ_win32パッチ", 103), + [FmgType.SystemTagsWindows] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.SystemTagsWindows, FmgType.SystemTagsWindows, "機種別タグ_win32", 91), + [FmgType.TalkMsg] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.TalkMsg, FmgType.TalkMsg, "会話", 1), + [FmgType.TalkMsg_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.TalkMsg_Patch, FmgType.TalkMsg, "会話パッチ", 104), + [FmgType.TextDisplayTagList] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.TextDisplayTagList, FmgType.TextDisplayTagList, "テキスト表示用タグ一覧", 90), + [FmgType.WeaponCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.WeaponCaption, FmgType.WeaponCaption, "武器うんちく", 25), + [FmgType.WeaponCaption_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.WeaponCaption_Patch, FmgType.WeaponCaption, "武器うんちくパッチ", 106), + [FmgType.WeaponInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.WeaponInfo, FmgType.WeaponInfo, "武器説明", 21), + [FmgType.WeaponInfo_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.WeaponInfo_Patch, FmgType.WeaponInfo, "武器説明パッチ", 114), + [FmgType.WeaponName] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Item, FmgType.WeaponName, FmgType.WeaponName, "武器名", 11), + [FmgType.WeaponName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsPtde, FmgCategory.Menu, FmgType.WeaponName_Patch, FmgType.WeaponName, "武器名パッチ", 115), + }, + Overrides = new() + { + [FmgType.AccessoryCaption] = new List { FmgType.AccessoryCaption_Patch }, + [FmgType.AccessoryInfo] = new List { FmgType.AccessoryInfo_Patch }, + [FmgType.AccessoryName] = new List { FmgType.AccessoryName_Patch }, + [FmgType.BloodMsg] = new List { FmgType.BloodMsg_Patch }, + [FmgType.Dialogues] = new List { FmgType.Dialogues_Patch }, + [FmgType.EventText] = new List { FmgType.EventText_Patch }, + [FmgType.GoodsCaption] = new List { FmgType.GoodsCaption_Patch }, + [FmgType.GoodsInfo] = new List { FmgType.GoodsInfo_Patch }, + [FmgType.GoodsName] = new List { FmgType.GoodsName_Patch }, + [FmgType.KeyGuide] = new List { FmgType.KeyGuide_Patch }, + [FmgType.LineHelp] = new List { FmgType.LineHelp_Patch }, + [FmgType.MagicCaption] = new List { FmgType.MagicCaption_Patch }, + [FmgType.MagicName] = new List { FmgType.MagicName_Patch }, + [FmgType.MenuGeneralText] = new List { FmgType.MenuGeneralText_Patch }, + [FmgType.MenuOther] = new List { FmgType.MenuOther_Patch }, + [FmgType.NpcName] = new List { FmgType.NpcName_Patch }, + [FmgType.PlaceName] = new List { FmgType.PlaceName_Patch }, + [FmgType.ProtectorCaption] = new List { FmgType.ProtectorCaption_Patch }, + [FmgType.ProtectorInfo] = new List { FmgType.ProtectorInfo_Patch }, + [FmgType.ProtectorName] = new List { FmgType.ProtectorName_Patch }, + [FmgType.SystemMessageWindows] = new List { FmgType.SystemMessageWindows_Patch }, + [FmgType.TalkMsg] = new List { FmgType.TalkMsg_Patch }, + [FmgType.WeaponCaption] = new List { FmgType.WeaponCaption_Patch }, + [FmgType.WeaponInfo] = new List { FmgType.WeaponInfo_Patch }, + [FmgType.WeaponName] = new List { FmgType.WeaponName_Patch }, + }, + ByFmgName = new() + { + ["NPC名"] = new List { FmgType.NpcName }, + ["NPC名パッチ"] = new List { FmgType.NpcName_Patch }, + ["アイテムうんちく"] = new List { FmgType.GoodsCaption }, + ["アイテムうんちくパッチ"] = new List { FmgType.GoodsCaption_Patch }, + ["アイテム名"] = new List { FmgType.GoodsName }, + ["アイテム名パッチ"] = new List { FmgType.GoodsName_Patch }, + ["アイテム説明"] = new List { FmgType.GoodsInfo }, + ["アイテム説明パッチ"] = new List { FmgType.GoodsInfo_Patch }, + ["アクセサリうんちく"] = new List { FmgType.AccessoryCaption }, + ["アクセサリうんちくパッチ"] = new List { FmgType.AccessoryCaption_Patch }, + ["アクセサリ名"] = new List { FmgType.AccessoryName }, + ["アクセサリ名パッチ"] = new List { FmgType.AccessoryName_Patch }, + ["アクセサリ説明"] = new List { FmgType.AccessoryInfo }, + ["アクセサリ説明パッチ"] = new List { FmgType.AccessoryInfo_Patch }, + ["イベントテキスト"] = new List { FmgType.EventText }, + ["イベントテキストパッチ"] = new List { FmgType.EventText_Patch }, + ["インゲームメニュー"] = new List { FmgType.InGameMenu }, + ["キーガイド"] = new List { FmgType.KeyGuide }, + ["キーガイドパッチ"] = new List { FmgType.KeyGuide_Patch }, + ["システムメッセージ_win32"] = new List { FmgType.SystemMessageWindows }, + ["システムメッセージ_win32パッチ"] = new List { FmgType.SystemMessageWindows_Patch }, + ["ダイアログ"] = new List { FmgType.Dialogues }, + ["ダイアログパッチ"] = new List { FmgType.Dialogues_Patch }, + ["テキスト表示用タグ一覧"] = new List { FmgType.TextDisplayTagList }, + ["ムービー字幕"] = new List { FmgType.MovieSubtitle }, + ["メニューその他"] = new List { FmgType.MenuOther }, + ["メニューその他パッチ"] = new List { FmgType.MenuOther_Patch }, + ["メニュー共通テキスト"] = new List { FmgType.MenuGeneralText }, + ["メニュー共通テキストパッチ"] = new List { FmgType.MenuGeneralText_Patch }, + ["一行ヘルプ"] = new List { FmgType.LineHelp }, + ["一行ヘルプパッチ"] = new List { FmgType.LineHelp_Patch }, + ["会話"] = new List { FmgType.TalkMsg }, + ["会話パッチ"] = new List { FmgType.TalkMsg_Patch }, + ["地名"] = new List { FmgType.PlaceName }, + ["地名パッチ"] = new List { FmgType.PlaceName_Patch }, + ["機種別タグ_win32"] = new List { FmgType.SystemTagsWindows }, + ["武器うんちく"] = new List { FmgType.WeaponCaption }, + ["武器うんちくパッチ"] = new List { FmgType.WeaponCaption_Patch }, + ["武器名"] = new List { FmgType.WeaponName }, + ["武器名パッチ"] = new List { FmgType.WeaponName_Patch }, + ["武器説明"] = new List { FmgType.WeaponInfo }, + ["武器説明パッチ"] = new List { FmgType.WeaponInfo_Patch }, + ["特徴うんちく"] = new List { FmgType.FeatureCaption }, + ["特徴名"] = new List { FmgType.FeatureName }, + ["特徴説明"] = new List { FmgType.FeatureInfo }, + ["血文字"] = new List { FmgType.BloodMsg }, + ["血文字パッチ"] = new List { FmgType.BloodMsg_Patch }, + ["防具うんちく"] = new List { FmgType.ProtectorCaption }, + ["防具うんちくパッチ"] = new List { FmgType.ProtectorCaption_Patch }, + ["防具名"] = new List { FmgType.ProtectorName }, + ["防具名パッチ"] = new List { FmgType.ProtectorName_Patch }, + ["防具説明"] = new List { FmgType.ProtectorInfo }, + ["防具説明パッチ"] = new List { FmgType.ProtectorInfo_Patch }, + ["項目ヘルプ"] = new List { FmgType.MenuContext }, + ["魔法うんちく"] = new List { FmgType.MagicCaption }, + ["魔法うんちくパッチ"] = new List { FmgType.MagicCaption_Patch }, + ["魔法名"] = new List { FmgType.MagicName }, + ["魔法名パッチ"] = new List { FmgType.MagicName_Patch }, + ["魔法説明"] = new List { FmgType.MagicInfo }, + }, + ByBinderID = new() + { + [1] = new List { FmgType.TalkMsg }, + [2] = new List { FmgType.BloodMsg }, + [3] = new List { FmgType.MovieSubtitle }, + [10] = new List { FmgType.GoodsName }, + [11] = new List { FmgType.WeaponName }, + [12] = new List { FmgType.ProtectorName }, + [13] = new List { FmgType.AccessoryName }, + [14] = new List { FmgType.MagicName }, + [15] = new List { FmgType.FeatureName }, + [16] = new List { FmgType.FeatureInfo }, + [17] = new List { FmgType.FeatureCaption }, + [18] = new List { FmgType.NpcName }, + [19] = new List { FmgType.PlaceName }, + [20] = new List { FmgType.GoodsInfo }, + [21] = new List { FmgType.WeaponInfo }, + [22] = new List { FmgType.ProtectorInfo }, + [23] = new List { FmgType.AccessoryInfo }, + [24] = new List { FmgType.GoodsCaption }, + [25] = new List { FmgType.WeaponCaption }, + [26] = new List { FmgType.ProtectorCaption }, + [27] = new List { FmgType.AccessoryCaption }, + [28] = new List { FmgType.MagicInfo }, + [29] = new List { FmgType.MagicCaption }, + [30] = new List { FmgType.EventText }, + [70] = new List { FmgType.InGameMenu }, + [76] = new List { FmgType.MenuGeneralText }, + [77] = new List { FmgType.MenuOther }, + [78] = new List { FmgType.Dialogues }, + [79] = new List { FmgType.KeyGuide }, + [80] = new List { FmgType.LineHelp }, + [81] = new List { FmgType.MenuContext }, + [90] = new List { FmgType.TextDisplayTagList }, + [91] = new List { FmgType.SystemTagsWindows }, + [92] = new List { FmgType.SystemMessageWindows }, + [100] = new List { FmgType.GoodsCaption_Patch }, + [101] = new List { FmgType.EventText_Patch }, + [102] = new List { FmgType.Dialogues_Patch }, + [103] = new List { FmgType.SystemMessageWindows_Patch }, + [104] = new List { FmgType.TalkMsg_Patch }, + [105] = new List { FmgType.MagicCaption_Patch }, + [106] = new List { FmgType.WeaponCaption_Patch }, + [107] = new List { FmgType.BloodMsg_Patch }, + [108] = new List { FmgType.ProtectorCaption_Patch }, + [109] = new List { FmgType.AccessoryCaption_Patch }, + [110] = new List { FmgType.GoodsInfo_Patch }, + [111] = new List { FmgType.GoodsName_Patch }, + [112] = new List { FmgType.AccessoryInfo_Patch }, + [113] = new List { FmgType.AccessoryName_Patch }, + [114] = new List { FmgType.WeaponInfo_Patch }, + [115] = new List { FmgType.WeaponName_Patch }, + [116] = new List { FmgType.ProtectorInfo_Patch }, + [117] = new List { FmgType.ProtectorName_Patch }, + [118] = new List { FmgType.MagicName_Patch }, + [119] = new List { FmgType.NpcName_Patch }, + [120] = new List { FmgType.PlaceName_Patch }, + [121] = new List { FmgType.LineHelp_Patch }, + [122] = new List { FmgType.KeyGuide_Patch }, + [123] = new List { FmgType.MenuOther_Patch }, + [124] = new List { FmgType.MenuGeneralText_Patch }, + }, + ToLanguageEnum = new() + { + ["english"] = FmgLanguage.English, + ["french"] = FmgLanguage.French, + ["german"] = FmgLanguage.German, + ["italian"] = FmgLanguage.Italian, + ["japanese"] = FmgLanguage.Japanese, + ["korean"] = FmgLanguage.Korean, + ["polish"] = FmgLanguage.Polish, + ["russian"] = FmgLanguage.Russian, + ["spanish"] = FmgLanguage.SpainSpanish, + ["tchinese"] = FmgLanguage.TraditionalChinese, + }, + FromLanguageEnum = new() + { + [FmgLanguage.English] = "english", + [FmgLanguage.French] = "french", + [FmgLanguage.German] = "german", + [FmgLanguage.Italian] = "italian", + [FmgLanguage.Japanese] = "japanese", + [FmgLanguage.Korean] = "korean", + [FmgLanguage.Polish] = "polish", + [FmgLanguage.Russian] = "russian", + [FmgLanguage.SpainSpanish] = "spanish", + [FmgLanguage.TraditionalChinese] = "tchinese", + }, + }, + [FromSoftGame.DarkSoulsRemastered] = new FmgGameInfo + { + ByType = new() + { + [FmgType.AccessoryCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.AccessoryCaption, FmgType.AccessoryCaption, "Accessory_long_desc_", 27), + [FmgType.AccessoryCaption_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.AccessoryCaption_Patch, FmgType.AccessoryCaption, "Accessory_long_desc_", 109), + [FmgType.AccessoryInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.AccessoryInfo, FmgType.AccessoryInfo, "Accessory_description_", 23), + [FmgType.AccessoryInfo_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.AccessoryInfo_Patch, FmgType.AccessoryInfo, "Accessory_description_", 112), + [FmgType.AccessoryName] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.AccessoryName, FmgType.AccessoryName, "Accessory_name_", 13), + [FmgType.AccessoryName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.AccessoryName_Patch, FmgType.AccessoryName, "Accessory_name_", 113), + [FmgType.BloodMsg] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.BloodMsg, FmgType.BloodMsg, "Blood_writing_", 2), + [FmgType.BloodMsg_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.BloodMsg_Patch, FmgType.BloodMsg, "Blood_writing_", 107), + [FmgType.Dialogues] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.Dialogues, FmgType.Dialogues, "Dialogue_", 78), + [FmgType.Dialogues_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.Dialogues_Patch, FmgType.Dialogues, "Dialogue_", 102), + [FmgType.EventText] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.EventText, FmgType.EventText, "Event_text_", 30), + [FmgType.EventText_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.EventText_Patch, FmgType.EventText, "Event_text_", 101), + [FmgType.FeatureCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.FeatureCaption, FmgType.FeatureCaption, "Feature_long_desc_", 17), + [FmgType.FeatureInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.FeatureInfo, FmgType.FeatureInfo, "Feature_description_", 16), + [FmgType.FeatureName] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.FeatureName, FmgType.FeatureName, "Feature_name_", 15), + [FmgType.GoodsCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.GoodsCaption, FmgType.GoodsCaption, "Item_long_desc_", 24), + [FmgType.GoodsCaption_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.GoodsCaption_Patch, FmgType.GoodsCaption, "Item_long_desc_", 100), + [FmgType.GoodsInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.GoodsInfo, FmgType.GoodsInfo, "Item_description_", 20), + [FmgType.GoodsInfo_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.GoodsInfo_Patch, FmgType.GoodsInfo, "Item_description_", 110), + [FmgType.GoodsName] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.GoodsName, FmgType.GoodsName, "Item_name_", 10), + [FmgType.GoodsName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.GoodsName_Patch, FmgType.GoodsName, "Item_name_", 111), + [FmgType.InGameMenu] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.InGameMenu, FmgType.InGameMenu, "Ingame_menu_", 70), + [FmgType.KeyGuide] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.KeyGuide, FmgType.KeyGuide, "Key_guide_", 79), + [FmgType.KeyGuide_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.KeyGuide_Patch, FmgType.KeyGuide, "Key_guide_", 122), + [FmgType.LineHelp] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.LineHelp, FmgType.LineHelp, "Single_line_help_", 80), + [FmgType.LineHelp_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.LineHelp_Patch, FmgType.LineHelp, "Single_line_help_", 121), + [FmgType.MagicCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.MagicCaption, FmgType.MagicCaption, "Magic_long_desc_", 29), + [FmgType.MagicCaption_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.MagicCaption_Patch, FmgType.MagicCaption, "Magic_long_desc_", 105), + [FmgType.MagicInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.MagicInfo, FmgType.MagicInfo, "Magic_description_", 28), + [FmgType.MagicName] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.MagicName, FmgType.MagicName, "Magic_name_", 14), + [FmgType.MagicName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.MagicName_Patch, FmgType.MagicName, "Magic_name_", 118), + [FmgType.MenuContext] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.MenuContext, FmgType.MenuContext, "Item_help_", 81), + [FmgType.MenuGeneralText] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.MenuGeneralText, FmgType.MenuGeneralText, "Menu_general_text_", 76), + [FmgType.MenuGeneralText_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.MenuGeneralText_Patch, FmgType.MenuGeneralText, "Menu_general_text_", 124), + [FmgType.MenuOther] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.MenuOther, FmgType.MenuOther, "Menu_others_", 77), + [FmgType.MenuOther_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.MenuOther_Patch, FmgType.MenuOther, "Menu_others_", 123), + [FmgType.MovieSubtitle] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.MovieSubtitle, FmgType.MovieSubtitle, "Movie_subtitles_", 3), + [FmgType.NpcName] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.NpcName, FmgType.NpcName, "NPC_name_", 18), + [FmgType.NpcName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.NpcName_Patch, FmgType.NpcName, "NPC_name_", 119), + [FmgType.PlaceName] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.PlaceName, FmgType.PlaceName, "Place_name_", 19), + [FmgType.PlaceName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.PlaceName_Patch, FmgType.PlaceName, "Place_name_", 120), + [FmgType.ProtectorCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.ProtectorCaption, FmgType.ProtectorCaption, "Armor_long_desc_", 26), + [FmgType.ProtectorCaption_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.ProtectorCaption_Patch, FmgType.ProtectorCaption, "Armor_long_desc_", 108), + [FmgType.ProtectorInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.ProtectorInfo, FmgType.ProtectorInfo, "Armor_description_", 22), + [FmgType.ProtectorInfo_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.ProtectorInfo_Patch, FmgType.ProtectorInfo, "Armor_description_", 116), + [FmgType.ProtectorName] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.ProtectorName, FmgType.ProtectorName, "Armor_name_", 12), + [FmgType.ProtectorName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.ProtectorName_Patch, FmgType.ProtectorName, "Armor_name_", 117), + [FmgType.SystemMessageWindows] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.SystemMessageWindows, FmgType.SystemMessageWindows, "System_message_win32_", 92), + [FmgType.SystemMessageWindows_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.SystemMessageWindows_Patch, FmgType.SystemMessageWindows, "System_message_win32_", 103), + [FmgType.SystemTagsWindows] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.SystemTagsWindows, FmgType.SystemTagsWindows, "System_specific_tags_win32_", 91), + [FmgType.TalkMsg] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.TalkMsg, FmgType.TalkMsg, "Conversation_", 1), + [FmgType.TalkMsg_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.TalkMsg_Patch, FmgType.TalkMsg, "Conversation_", 104), + [FmgType.TextDisplayTagList] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Menu, FmgType.TextDisplayTagList, FmgType.TextDisplayTagList, "Text_display_tag_list_", 90), + [FmgType.WeaponCaption] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.WeaponCaption, FmgType.WeaponCaption, "Weapon_long_desc_", 25), + [FmgType.WeaponCaption_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.WeaponCaption_Patch, FmgType.WeaponCaption, "Weapon_long_desc_", 106), + [FmgType.WeaponInfo] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.WeaponInfo, FmgType.WeaponInfo, "Weapon_description_", 21), + [FmgType.WeaponInfo_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.WeaponInfo_Patch, FmgType.WeaponInfo, "Weapon_description_", 114), + [FmgType.WeaponName] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.WeaponName, FmgType.WeaponName, "Weapon_name_", 11), + [FmgType.WeaponName_Patch] = new FmgKeyInfo(FromSoftGame.DarkSoulsRemastered, FmgCategory.Item, FmgType.WeaponName_Patch, FmgType.WeaponName, "Weapon_name_", 115), + }, + Overrides = new() + { + [FmgType.AccessoryCaption] = new List { FmgType.AccessoryCaption_Patch }, + [FmgType.AccessoryInfo] = new List { FmgType.AccessoryInfo_Patch }, + [FmgType.AccessoryName] = new List { FmgType.AccessoryName_Patch }, + [FmgType.BloodMsg] = new List { FmgType.BloodMsg_Patch }, + [FmgType.Dialogues] = new List { FmgType.Dialogues_Patch }, + [FmgType.EventText] = new List { FmgType.EventText_Patch }, + [FmgType.GoodsCaption] = new List { FmgType.GoodsCaption_Patch }, + [FmgType.GoodsInfo] = new List { FmgType.GoodsInfo_Patch }, + [FmgType.GoodsName] = new List { FmgType.GoodsName_Patch }, + [FmgType.KeyGuide] = new List { FmgType.KeyGuide_Patch }, + [FmgType.LineHelp] = new List { FmgType.LineHelp_Patch }, + [FmgType.MagicCaption] = new List { FmgType.MagicCaption_Patch }, + [FmgType.MagicName] = new List { FmgType.MagicName_Patch }, + [FmgType.MenuGeneralText] = new List { FmgType.MenuGeneralText_Patch }, + [FmgType.MenuOther] = new List { FmgType.MenuOther_Patch }, + [FmgType.NpcName] = new List { FmgType.NpcName_Patch }, + [FmgType.PlaceName] = new List { FmgType.PlaceName_Patch }, + [FmgType.ProtectorCaption] = new List { FmgType.ProtectorCaption_Patch }, + [FmgType.ProtectorInfo] = new List { FmgType.ProtectorInfo_Patch }, + [FmgType.ProtectorName] = new List { FmgType.ProtectorName_Patch }, + [FmgType.SystemMessageWindows] = new List { FmgType.SystemMessageWindows_Patch }, + [FmgType.TalkMsg] = new List { FmgType.TalkMsg_Patch }, + [FmgType.WeaponCaption] = new List { FmgType.WeaponCaption_Patch }, + [FmgType.WeaponInfo] = new List { FmgType.WeaponInfo_Patch }, + [FmgType.WeaponName] = new List { FmgType.WeaponName_Patch }, + }, + ByFmgName = new() + { + ["Accessory_description_"] = new List { FmgType.AccessoryInfo, FmgType.AccessoryInfo_Patch }, + ["Accessory_long_desc_"] = new List { FmgType.AccessoryCaption, FmgType.AccessoryCaption_Patch }, + ["Accessory_name_"] = new List { FmgType.AccessoryName, FmgType.AccessoryName_Patch }, + ["Armor_description_"] = new List { FmgType.ProtectorInfo, FmgType.ProtectorInfo_Patch }, + ["Armor_long_desc_"] = new List { FmgType.ProtectorCaption, FmgType.ProtectorCaption_Patch }, + ["Armor_name_"] = new List { FmgType.ProtectorName, FmgType.ProtectorName_Patch }, + ["Blood_writing_"] = new List { FmgType.BloodMsg, FmgType.BloodMsg_Patch }, + ["Conversation_"] = new List { FmgType.TalkMsg, FmgType.TalkMsg_Patch }, + ["Dialogue_"] = new List { FmgType.Dialogues, FmgType.Dialogues_Patch }, + ["Event_text_"] = new List { FmgType.EventText, FmgType.EventText_Patch }, + ["Feature_description_"] = new List { FmgType.FeatureInfo }, + ["Feature_long_desc_"] = new List { FmgType.FeatureCaption }, + ["Feature_name_"] = new List { FmgType.FeatureName }, + ["Ingame_menu_"] = new List { FmgType.InGameMenu }, + ["Item_description_"] = new List { FmgType.GoodsInfo, FmgType.GoodsInfo_Patch }, + ["Item_help_"] = new List { FmgType.MenuContext }, + ["Item_long_desc_"] = new List { FmgType.GoodsCaption, FmgType.GoodsCaption_Patch }, + ["Item_name_"] = new List { FmgType.GoodsName, FmgType.GoodsName_Patch }, + ["Key_guide_"] = new List { FmgType.KeyGuide, FmgType.KeyGuide_Patch }, + ["Magic_description_"] = new List { FmgType.MagicInfo }, + ["Magic_long_desc_"] = new List { FmgType.MagicCaption, FmgType.MagicCaption_Patch }, + ["Magic_name_"] = new List { FmgType.MagicName, FmgType.MagicName_Patch }, + ["Menu_general_text_"] = new List { FmgType.MenuGeneralText, FmgType.MenuGeneralText_Patch }, + ["Menu_others_"] = new List { FmgType.MenuOther, FmgType.MenuOther_Patch }, + ["Movie_subtitles_"] = new List { FmgType.MovieSubtitle }, + ["NPC_name_"] = new List { FmgType.NpcName, FmgType.NpcName_Patch }, + ["Place_name_"] = new List { FmgType.PlaceName, FmgType.PlaceName_Patch }, + ["Single_line_help_"] = new List { FmgType.LineHelp, FmgType.LineHelp_Patch }, + ["System_message_win32_"] = new List { FmgType.SystemMessageWindows, FmgType.SystemMessageWindows_Patch }, + ["System_specific_tags_win32_"] = new List { FmgType.SystemTagsWindows }, + ["Text_display_tag_list_"] = new List { FmgType.TextDisplayTagList }, + ["Weapon_description_"] = new List { FmgType.WeaponInfo, FmgType.WeaponInfo_Patch }, + ["Weapon_long_desc_"] = new List { FmgType.WeaponCaption, FmgType.WeaponCaption_Patch }, + ["Weapon_name_"] = new List { FmgType.WeaponName, FmgType.WeaponName_Patch }, + }, + ByBinderID = new() + { + [1] = new List { FmgType.TalkMsg }, + [2] = new List { FmgType.BloodMsg }, + [3] = new List { FmgType.MovieSubtitle }, + [10] = new List { FmgType.GoodsName }, + [11] = new List { FmgType.WeaponName }, + [12] = new List { FmgType.ProtectorName }, + [13] = new List { FmgType.AccessoryName }, + [14] = new List { FmgType.MagicName }, + [15] = new List { FmgType.FeatureName }, + [16] = new List { FmgType.FeatureInfo }, + [17] = new List { FmgType.FeatureCaption }, + [18] = new List { FmgType.NpcName }, + [19] = new List { FmgType.PlaceName }, + [20] = new List { FmgType.GoodsInfo }, + [21] = new List { FmgType.WeaponInfo }, + [22] = new List { FmgType.ProtectorInfo }, + [23] = new List { FmgType.AccessoryInfo }, + [24] = new List { FmgType.GoodsCaption }, + [25] = new List { FmgType.WeaponCaption }, + [26] = new List { FmgType.ProtectorCaption }, + [27] = new List { FmgType.AccessoryCaption }, + [28] = new List { FmgType.MagicInfo }, + [29] = new List { FmgType.MagicCaption }, + [30] = new List { FmgType.EventText }, + [70] = new List { FmgType.InGameMenu }, + [76] = new List { FmgType.MenuGeneralText }, + [77] = new List { FmgType.MenuOther }, + [78] = new List { FmgType.Dialogues }, + [79] = new List { FmgType.KeyGuide }, + [80] = new List { FmgType.LineHelp }, + [81] = new List { FmgType.MenuContext }, + [90] = new List { FmgType.TextDisplayTagList }, + [91] = new List { FmgType.SystemTagsWindows }, + [92] = new List { FmgType.SystemMessageWindows }, + [100] = new List { FmgType.GoodsCaption_Patch }, + [101] = new List { FmgType.EventText_Patch }, + [102] = new List { FmgType.Dialogues_Patch }, + [103] = new List { FmgType.SystemMessageWindows_Patch }, + [104] = new List { FmgType.TalkMsg_Patch }, + [105] = new List { FmgType.MagicCaption_Patch }, + [106] = new List { FmgType.WeaponCaption_Patch }, + [107] = new List { FmgType.BloodMsg_Patch }, + [108] = new List { FmgType.ProtectorCaption_Patch }, + [109] = new List { FmgType.AccessoryCaption_Patch }, + [110] = new List { FmgType.GoodsInfo_Patch }, + [111] = new List { FmgType.GoodsName_Patch }, + [112] = new List { FmgType.AccessoryInfo_Patch }, + [113] = new List { FmgType.AccessoryName_Patch }, + [114] = new List { FmgType.WeaponInfo_Patch }, + [115] = new List { FmgType.WeaponName_Patch }, + [116] = new List { FmgType.ProtectorInfo_Patch }, + [117] = new List { FmgType.ProtectorName_Patch }, + [118] = new List { FmgType.MagicName_Patch }, + [119] = new List { FmgType.NpcName_Patch }, + [120] = new List { FmgType.PlaceName_Patch }, + [121] = new List { FmgType.LineHelp_Patch }, + [122] = new List { FmgType.KeyGuide_Patch }, + [123] = new List { FmgType.MenuOther_Patch }, + [124] = new List { FmgType.MenuGeneralText_Patch }, + }, + ToLanguageEnum = new() + { + ["english"] = FmgLanguage.English, + ["french"] = FmgLanguage.French, + ["german"] = FmgLanguage.German, + ["italian"] = FmgLanguage.Italian, + ["japanese"] = FmgLanguage.Japanese, + ["korean"] = FmgLanguage.Korean, + ["nspanish"] = FmgLanguage.Spanish, + ["polish"] = FmgLanguage.Polish, + ["portuguese"] = FmgLanguage.BrazilPortuguese, + ["russian"] = FmgLanguage.Russian, + ["schinese"] = FmgLanguage.SimplifiedChinese, + ["spanish"] = FmgLanguage.SpainSpanish, + ["tchinese"] = FmgLanguage.TraditionalChinese, + }, + FromLanguageEnum = new() + { + [FmgLanguage.English] = "english", + [FmgLanguage.French] = "french", + [FmgLanguage.German] = "german", + [FmgLanguage.Italian] = "italian", + [FmgLanguage.Japanese] = "japanese", + [FmgLanguage.Korean] = "korean", + [FmgLanguage.Spanish] = "nspanish", + [FmgLanguage.Polish] = "polish", + [FmgLanguage.BrazilPortuguese] = "portuguese", + [FmgLanguage.Russian] = "russian", + [FmgLanguage.SimplifiedChinese] = "schinese", + [FmgLanguage.SpainSpanish] = "spanish", + [FmgLanguage.TraditionalChinese] = "tchinese", + }, + }, + [FromSoftGame.DarkSouls2] = new FmgGameInfo + { + ByType = new() + { + [FmgType.BloodMsgConjunction] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.BloodMsgConjunction, FmgType.BloodMsgConjunction, "bloodmessageconjunction", -1), + [FmgType.BloodMsgSentence] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.BloodMsgSentence, FmgType.BloodMsgSentence, "bloodmessagesentence", -1), + [FmgType.BloodMsgWord] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.BloodMsgWord, FmgType.BloodMsgWord, "bloodmessageword", -1), + [FmgType.BloodMsgWordCategory] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.BloodMsgWordCategory, FmgType.BloodMsgWordCategory, "bloodmessagewordcategory", -1), + [FmgType.BonfireMenu] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.BonfireMenu, FmgType.BonfireMenu, "bofire", -1), + [FmgType.BonfireName] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.BonfireName, FmgType.BonfireName, "bonfirename", -1), + [FmgType.CharaMaking] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.CharaMaking, FmgType.CharaMaking, "charamaking", -1), + [FmgType.CharaName] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.CharaName, FmgType.CharaName, "charaname", -1), + [FmgType.DS2Common] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.DS2Common, FmgType.DS2Common, "common", -1), + [FmgType.DS2Shop] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.DS2Shop, FmgType.DS2Shop, "shop", -1), + [FmgType.EventTextForMap] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.EventTextForMap, FmgType.EventTextForMap, "mapevent", -1), + [FmgType.IconHelp] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.IconHelp, FmgType.IconHelp, "iconhelp", -1), + [FmgType.InGameMenu] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.InGameMenu, FmgType.InGameMenu, "ingamemenu", -1), + [FmgType.InGameSystem] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.InGameSystem, FmgType.InGameSystem, "ingamesystem", -1), + [FmgType.ItemCaption] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.ItemCaption, FmgType.ItemCaption, "detailedexplanation", -1), + [FmgType.ItemInfo] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.ItemInfo, FmgType.ItemInfo, "simpleexplanation", -1), + [FmgType.ItemName] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.ItemName, FmgType.ItemName, "itemname", -1), + [FmgType.KeyGuide] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.KeyGuide, FmgType.KeyGuide, "keyguide", -1), + [FmgType.NpcMenu] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.NpcMenu, FmgType.NpcMenu, "npcmenu", -1), + [FmgType.PlaceName] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.PlaceName, FmgType.PlaceName, "mapname", -1), + [FmgType.PluralSelect] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.PluralSelect, FmgType.PluralSelect, "pluralselect", -1), + [FmgType.Prologue] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.Prologue, FmgType.Prologue, "prologue", -1), + [FmgType.StaffRoll] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.StaffRoll, FmgType.StaffRoll, "staffroll", -1), + [FmgType.SystemMessageWindows] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.SystemMessageWindows, FmgType.SystemMessageWindows, "win32onlymessage", -1), + [FmgType.TitleFlow] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.TitleFlow, FmgType.TitleFlow, "titleflow", -1), + [FmgType.TitleMenu] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.TitleMenu, FmgType.TitleMenu, "titlemenu", -1), + [FmgType.WeaponType] = new FmgKeyInfo(FromSoftGame.DarkSouls2, FmgCategory.None, FmgType.WeaponType, FmgType.WeaponType, "weapontype", -1), + }, + Overrides = new() + { + + }, + ByFmgName = new() + { + ["bloodmessageconjunction"] = new List { FmgType.BloodMsgConjunction }, + ["bloodmessagesentence"] = new List { FmgType.BloodMsgSentence }, + ["bloodmessageword"] = new List { FmgType.BloodMsgWord }, + ["bloodmessagewordcategory"] = new List { FmgType.BloodMsgWordCategory }, + ["bofire"] = new List { FmgType.BonfireMenu }, + ["bonfirename"] = new List { FmgType.BonfireName }, + ["charamaking"] = new List { FmgType.CharaMaking }, + ["charaname"] = new List { FmgType.CharaName }, + ["common"] = new List { FmgType.DS2Common }, + ["detailedexplanation"] = new List { FmgType.ItemCaption }, + ["iconhelp"] = new List { FmgType.IconHelp }, + ["ingamemenu"] = new List { FmgType.InGameMenu }, + ["ingamesystem"] = new List { FmgType.InGameSystem }, + ["itemname"] = new List { FmgType.ItemName }, + ["keyguide"] = new List { FmgType.KeyGuide }, + ["mapevent"] = new List { FmgType.EventTextForMap }, + ["mapname"] = new List { FmgType.PlaceName }, + ["npcmenu"] = new List { FmgType.NpcMenu }, + ["pluralselect"] = new List { FmgType.PluralSelect }, + ["prologue"] = new List { FmgType.Prologue }, + ["shop"] = new List { FmgType.DS2Shop }, + ["simpleexplanation"] = new List { FmgType.ItemInfo }, + ["staffroll"] = new List { FmgType.StaffRoll }, + ["titleflow"] = new List { FmgType.TitleFlow }, + ["titlemenu"] = new List { FmgType.TitleMenu }, + ["weapontype"] = new List { FmgType.WeaponType }, + ["win32onlymessage"] = new List { FmgType.SystemMessageWindows }, + }, + ByBinderID = new() + { + + }, + ToLanguageEnum = new() + { + ["chinese"] = FmgLanguage.TraditionalChinese, + ["english"] = FmgLanguage.English, + ["french"] = FmgLanguage.French, + ["germany"] = FmgLanguage.German, + ["italian"] = FmgLanguage.Italian, + ["japanese"] = FmgLanguage.Japanese, + ["korean"] = FmgLanguage.Korean, + ["polish"] = FmgLanguage.Polish, + ["portuguese"] = FmgLanguage.BrazilPortuguese, + ["russian"] = FmgLanguage.Russian, + ["spanish"] = FmgLanguage.SpainSpanish, + }, + FromLanguageEnum = new() + { + [FmgLanguage.TraditionalChinese] = "chinese", + [FmgLanguage.English] = "english", + [FmgLanguage.French] = "french", + [FmgLanguage.German] = "germany", + [FmgLanguage.Italian] = "italian", + [FmgLanguage.Japanese] = "japanese", + [FmgLanguage.Korean] = "korean", + [FmgLanguage.Polish] = "polish", + [FmgLanguage.BrazilPortuguese] = "portuguese", + [FmgLanguage.Russian] = "russian", + [FmgLanguage.SpainSpanish] = "spanish", + }, + }, + [FromSoftGame.DarkSouls2Sotfs] = new FmgGameInfo + { + ByType = new() + { + [FmgType.BloodMsgConjunction] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.BloodMsgConjunction, FmgType.BloodMsgConjunction, "bloodmessageconjunction", -1), + [FmgType.BloodMsgSentence] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.BloodMsgSentence, FmgType.BloodMsgSentence, "bloodmessagesentence", -1), + [FmgType.BloodMsgWord] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.BloodMsgWord, FmgType.BloodMsgWord, "bloodmessageword", -1), + [FmgType.BloodMsgWordCategory] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.BloodMsgWordCategory, FmgType.BloodMsgWordCategory, "bloodmessagewordcategory", -1), + [FmgType.BonfireMenu] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.BonfireMenu, FmgType.BonfireMenu, "bofire", -1), + [FmgType.BonfireName] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.BonfireName, FmgType.BonfireName, "bonfirename", -1), + [FmgType.CharaMaking] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.CharaMaking, FmgType.CharaMaking, "charamaking", -1), + [FmgType.CharaName] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.CharaName, FmgType.CharaName, "charaname", -1), + [FmgType.DS2Common] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.DS2Common, FmgType.DS2Common, "common", -1), + [FmgType.DS2Shop] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.DS2Shop, FmgType.DS2Shop, "shop", -1), + [FmgType.EventTextForMap] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.EventTextForMap, FmgType.EventTextForMap, "mapevent", -1), + [FmgType.IconHelp] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.IconHelp, FmgType.IconHelp, "iconhelp", -1), + [FmgType.InGameMenu] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.InGameMenu, FmgType.InGameMenu, "ingamemenu", -1), + [FmgType.InGameSystem] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.InGameSystem, FmgType.InGameSystem, "ingamesystem", -1), + [FmgType.ItemCaption] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.ItemCaption, FmgType.ItemCaption, "detailedexplanation", -1), + [FmgType.ItemInfo] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.ItemInfo, FmgType.ItemInfo, "simpleexplanation", -1), + [FmgType.ItemName] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.ItemName, FmgType.ItemName, "itemname", -1), + [FmgType.KeyGuide] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.KeyGuide, FmgType.KeyGuide, "keyguide", -1), + [FmgType.NpcMenu] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.NpcMenu, FmgType.NpcMenu, "npcmenu", -1), + [FmgType.PlaceName] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.PlaceName, FmgType.PlaceName, "mapname", -1), + [FmgType.PluralSelect] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.PluralSelect, FmgType.PluralSelect, "pluralselect", -1), + [FmgType.Prologue] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.Prologue, FmgType.Prologue, "prologue", -1), + [FmgType.StaffRoll] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.StaffRoll, FmgType.StaffRoll, "staffroll", -1), + [FmgType.SystemMessageDC] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.SystemMessageDC, FmgType.SystemMessageDC, "dconlymessage", -1), + [FmgType.SystemMessageWindows] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.SystemMessageWindows, FmgType.SystemMessageWindows, "win32onlymessage", -1), + [FmgType.TitleFlow] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.TitleFlow, FmgType.TitleFlow, "titleflow", -1), + [FmgType.TitleMenu] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.TitleMenu, FmgType.TitleMenu, "titlemenu", -1), + [FmgType.WeaponType] = new FmgKeyInfo(FromSoftGame.DarkSouls2Sotfs, FmgCategory.None, FmgType.WeaponType, FmgType.WeaponType, "weapontype", -1), + }, + Overrides = new() + { + + }, + ByFmgName = new() + { + ["bloodmessageconjunction"] = new List { FmgType.BloodMsgConjunction }, + ["bloodmessagesentence"] = new List { FmgType.BloodMsgSentence }, + ["bloodmessageword"] = new List { FmgType.BloodMsgWord }, + ["bloodmessagewordcategory"] = new List { FmgType.BloodMsgWordCategory }, + ["bofire"] = new List { FmgType.BonfireMenu }, + ["bonfirename"] = new List { FmgType.BonfireName }, + ["charamaking"] = new List { FmgType.CharaMaking }, + ["charaname"] = new List { FmgType.CharaName }, + ["common"] = new List { FmgType.DS2Common }, + ["dconlymessage"] = new List { FmgType.SystemMessageDC }, + ["detailedexplanation"] = new List { FmgType.ItemCaption }, + ["iconhelp"] = new List { FmgType.IconHelp }, + ["ingamemenu"] = new List { FmgType.InGameMenu }, + ["ingamesystem"] = new List { FmgType.InGameSystem }, + ["itemname"] = new List { FmgType.ItemName }, + ["keyguide"] = new List { FmgType.KeyGuide }, + ["mapevent"] = new List { FmgType.EventTextForMap }, + ["mapname"] = new List { FmgType.PlaceName }, + ["npcmenu"] = new List { FmgType.NpcMenu }, + ["pluralselect"] = new List { FmgType.PluralSelect }, + ["prologue"] = new List { FmgType.Prologue }, + ["shop"] = new List { FmgType.DS2Shop }, + ["simpleexplanation"] = new List { FmgType.ItemInfo }, + ["staffroll"] = new List { FmgType.StaffRoll }, + ["titleflow"] = new List { FmgType.TitleFlow }, + ["titlemenu"] = new List { FmgType.TitleMenu }, + ["weapontype"] = new List { FmgType.WeaponType }, + ["win32onlymessage"] = new List { FmgType.SystemMessageWindows }, + }, + ByBinderID = new() + { + + }, + ToLanguageEnum = new() + { + ["chinese"] = FmgLanguage.TraditionalChinese, + ["english"] = FmgLanguage.English, + ["french"] = FmgLanguage.French, + ["germany"] = FmgLanguage.German, + ["italian"] = FmgLanguage.Italian, + ["japanese"] = FmgLanguage.Japanese, + ["korean"] = FmgLanguage.Korean, + ["neutralspanish"] = FmgLanguage.Spanish, + ["polish"] = FmgLanguage.Polish, + ["portuguese"] = FmgLanguage.BrazilPortuguese, + ["russian"] = FmgLanguage.Russian, + ["spanish"] = FmgLanguage.SpainSpanish, + }, + FromLanguageEnum = new() + { + [FmgLanguage.TraditionalChinese] = "chinese", + [FmgLanguage.English] = "english", + [FmgLanguage.French] = "french", + [FmgLanguage.German] = "germany", + [FmgLanguage.Italian] = "italian", + [FmgLanguage.Japanese] = "japanese", + [FmgLanguage.Korean] = "korean", + [FmgLanguage.Spanish] = "neutralspanish", + [FmgLanguage.Polish] = "polish", + [FmgLanguage.BrazilPortuguese] = "portuguese", + [FmgLanguage.Russian] = "russian", + [FmgLanguage.SpainSpanish] = "spanish", + }, + }, + [FromSoftGame.Bloodborne] = new FmgGameInfo + { + ByType = new() + { + [FmgType.AccessoryCaption] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.AccessoryCaption, FmgType.AccessoryCaption, "アクセサリうんちく", 27), + [FmgType.AccessoryInfo] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.AccessoryInfo, FmgType.AccessoryInfo, "アクセサリ説明", 23), + [FmgType.AccessoryName] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.AccessoryName, FmgType.AccessoryName, "アクセサリ名", 13), + [FmgType.BloodMsg] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.BloodMsg, FmgType.BloodMsg, "血文字", 2), + [FmgType.CauseOfDeath] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.CauseOfDeath, FmgType.CauseOfDeath, "死因", 4), + [FmgType.Dialogues] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.Dialogues, FmgType.Dialogues, "ダイアログ", 78), + [FmgType.EventText] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.EventText, FmgType.EventText, "イベントテキスト", 30), + [FmgType.GameDialogues] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.GameDialogues, FmgType.GameDialogues, "SP_ダイアログ", 204), + [FmgType.GameKeyGuide] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.GameKeyGuide, FmgType.GameKeyGuide, "SP_キーガイド", 202), + [FmgType.GameLineHelp] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.GameLineHelp, FmgType.GameLineHelp, "SP_一行ヘルプ", 201), + [FmgType.GameMenuText] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.GameMenuText, FmgType.GameMenuText, "SP_メニューテキスト", 200), + [FmgType.GameSystemMessageWindows] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.GameSystemMessageWindows, FmgType.GameSystemMessageWindows, "SP_システムメッセージ_win64", 203), + [FmgType.GemCaption] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.GemCaption, FmgType.GemCaption, "魔石うんちく", 33), + [FmgType.GemEffect] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.GemEffect, FmgType.GemEffect, "魔石効果", 35), + [FmgType.GemInfo] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.GemInfo, FmgType.GemInfo, "魔石説明", 32), + [FmgType.GemName] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.GemName, FmgType.GemName, "魔石名", 31), + [FmgType.GemPrefix] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.GemPrefix, FmgType.GemPrefix, "魔石接頭語", 34), + [FmgType.GoodsCaption] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.GoodsCaption, FmgType.GoodsCaption, "アイテムうんちく", 24), + [FmgType.GoodsInfo] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.GoodsInfo, FmgType.GoodsInfo, "アイテム説明", 20), + [FmgType.GoodsName] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.GoodsName, FmgType.GoodsName, "アイテム名", 10), + [FmgType.InGameMenu] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.InGameMenu, FmgType.InGameMenu, "インゲームメニュー", 70), + [FmgType.KeyGuide] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.KeyGuide, FmgType.KeyGuide, "キーガイド", 79), + [FmgType.LineHelp] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.LineHelp, FmgType.LineHelp, "一行ヘルプ", 80), + [FmgType.MagicCaption] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.MagicCaption, FmgType.MagicCaption, "魔法うんちく", 29), + [FmgType.MagicInfo] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.MagicInfo, FmgType.MagicInfo, "魔法説明", 28), + [FmgType.MagicName] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.MagicName, FmgType.MagicName, "魔法名", 14), + [FmgType.MenuGeneralText] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.MenuGeneralText, FmgType.MenuGeneralText, "メニュー共通テキスト", 76), + [FmgType.MenuOther] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.MenuOther, FmgType.MenuOther, "メニューその他", 77), + [FmgType.MovieSubtitle] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.MovieSubtitle, FmgType.MovieSubtitle, "ムービー字幕", 3), + [FmgType.NpcName] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.NpcName, FmgType.NpcName, "NPC名", 18), + [FmgType.PlaceName] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.PlaceName, FmgType.PlaceName, "地名", 19), + [FmgType.ProtectorCaption] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.ProtectorCaption, FmgType.ProtectorCaption, "防具うんちく", 26), + [FmgType.ProtectorInfo] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.ProtectorInfo, FmgType.ProtectorInfo, "防具説明", 22), + [FmgType.ProtectorName] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.ProtectorName, FmgType.ProtectorName, "防具名", 12), + [FmgType.SystemMessageWindows] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.SystemMessageWindows, FmgType.SystemMessageWindows, "システムメッセージ_win64", 92), + [FmgType.SystemTagsWindows] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.SystemTagsWindows, FmgType.SystemTagsWindows, "機種別タグ_win64", 91), + [FmgType.TalkMsg] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.TalkMsg, FmgType.TalkMsg, "会話", 1), + [FmgType.TextDisplayTagList] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Menu, FmgType.TextDisplayTagList, FmgType.TextDisplayTagList, "テキスト表示用タグ一覧", 90), + [FmgType.WeaponCaption] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.WeaponCaption, FmgType.WeaponCaption, "武器うんちく", 25), + [FmgType.WeaponInfo] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.WeaponInfo, FmgType.WeaponInfo, "武器説明", 21), + [FmgType.WeaponName] = new FmgKeyInfo(FromSoftGame.Bloodborne, FmgCategory.Item, FmgType.WeaponName, FmgType.WeaponName, "武器名", 11), + }, + Overrides = new() + { + + }, + ByFmgName = new() + { + ["NPC名"] = new List { FmgType.NpcName }, + ["SP_キーガイド"] = new List { FmgType.GameKeyGuide }, + ["SP_システムメッセージ_win64"] = new List { FmgType.GameSystemMessageWindows }, + ["SP_ダイアログ"] = new List { FmgType.GameDialogues }, + ["SP_メニューテキスト"] = new List { FmgType.GameMenuText }, + ["SP_一行ヘルプ"] = new List { FmgType.GameLineHelp }, + ["アイテムうんちく"] = new List { FmgType.GoodsCaption }, + ["アイテム名"] = new List { FmgType.GoodsName }, + ["アイテム説明"] = new List { FmgType.GoodsInfo }, + ["アクセサリうんちく"] = new List { FmgType.AccessoryCaption }, + ["アクセサリ名"] = new List { FmgType.AccessoryName }, + ["アクセサリ説明"] = new List { FmgType.AccessoryInfo }, + ["イベントテキスト"] = new List { FmgType.EventText }, + ["インゲームメニュー"] = new List { FmgType.InGameMenu }, + ["キーガイド"] = new List { FmgType.KeyGuide }, + ["システムメッセージ_win64"] = new List { FmgType.SystemMessageWindows }, + ["ダイアログ"] = new List { FmgType.Dialogues }, + ["テキスト表示用タグ一覧"] = new List { FmgType.TextDisplayTagList }, + ["ムービー字幕"] = new List { FmgType.MovieSubtitle }, + ["メニューその他"] = new List { FmgType.MenuOther }, + ["メニュー共通テキスト"] = new List { FmgType.MenuGeneralText }, + ["一行ヘルプ"] = new List { FmgType.LineHelp }, + ["会話"] = new List { FmgType.TalkMsg }, + ["地名"] = new List { FmgType.PlaceName }, + ["機種別タグ_win64"] = new List { FmgType.SystemTagsWindows }, + ["武器うんちく"] = new List { FmgType.WeaponCaption }, + ["武器名"] = new List { FmgType.WeaponName }, + ["武器説明"] = new List { FmgType.WeaponInfo }, + ["死因"] = new List { FmgType.CauseOfDeath }, + ["血文字"] = new List { FmgType.BloodMsg }, + ["防具うんちく"] = new List { FmgType.ProtectorCaption }, + ["防具名"] = new List { FmgType.ProtectorName }, + ["防具説明"] = new List { FmgType.ProtectorInfo }, + ["魔法うんちく"] = new List { FmgType.MagicCaption }, + ["魔法名"] = new List { FmgType.MagicName }, + ["魔法説明"] = new List { FmgType.MagicInfo }, + ["魔石うんちく"] = new List { FmgType.GemCaption }, + ["魔石効果"] = new List { FmgType.GemEffect }, + ["魔石名"] = new List { FmgType.GemName }, + ["魔石接頭語"] = new List { FmgType.GemPrefix }, + ["魔石説明"] = new List { FmgType.GemInfo }, + }, + ByBinderID = new() + { + [1] = new List { FmgType.TalkMsg }, + [2] = new List { FmgType.BloodMsg }, + [3] = new List { FmgType.MovieSubtitle }, + [4] = new List { FmgType.CauseOfDeath }, + [10] = new List { FmgType.GoodsName }, + [11] = new List { FmgType.WeaponName }, + [12] = new List { FmgType.ProtectorName }, + [13] = new List { FmgType.AccessoryName }, + [14] = new List { FmgType.MagicName }, + [18] = new List { FmgType.NpcName }, + [19] = new List { FmgType.PlaceName }, + [20] = new List { FmgType.GoodsInfo }, + [21] = new List { FmgType.WeaponInfo }, + [22] = new List { FmgType.ProtectorInfo }, + [23] = new List { FmgType.AccessoryInfo }, + [24] = new List { FmgType.GoodsCaption }, + [25] = new List { FmgType.WeaponCaption }, + [26] = new List { FmgType.ProtectorCaption }, + [27] = new List { FmgType.AccessoryCaption }, + [28] = new List { FmgType.MagicInfo }, + [29] = new List { FmgType.MagicCaption }, + [30] = new List { FmgType.EventText }, + [31] = new List { FmgType.GemName }, + [32] = new List { FmgType.GemInfo }, + [33] = new List { FmgType.GemCaption }, + [34] = new List { FmgType.GemPrefix }, + [35] = new List { FmgType.GemEffect }, + [70] = new List { FmgType.InGameMenu }, + [76] = new List { FmgType.MenuGeneralText }, + [77] = new List { FmgType.MenuOther }, + [78] = new List { FmgType.Dialogues }, + [79] = new List { FmgType.KeyGuide }, + [80] = new List { FmgType.LineHelp }, + [90] = new List { FmgType.TextDisplayTagList }, + [91] = new List { FmgType.SystemTagsWindows }, + [92] = new List { FmgType.SystemMessageWindows }, + [200] = new List { FmgType.GameMenuText }, + [201] = new List { FmgType.GameLineHelp }, + [202] = new List { FmgType.GameKeyGuide }, + [203] = new List { FmgType.GameSystemMessageWindows }, + [204] = new List { FmgType.GameDialogues }, + }, + ToLanguageEnum = new() + { + ["dandk"] = FmgLanguage.Danish, + ["deude"] = FmgLanguage.German, + ["enggb"] = FmgLanguage.BritishEnglish, + ["engus"] = FmgLanguage.English, + ["finfi"] = FmgLanguage.Finnish, + ["frafr"] = FmgLanguage.French, + ["itait"] = FmgLanguage.Italian, + ["jpnjp"] = FmgLanguage.Japanese, + ["korkr"] = FmgLanguage.Korean, + ["nldnl"] = FmgLanguage.Dutch, + ["norno"] = FmgLanguage.Norwegian, + ["polpl"] = FmgLanguage.Polish, + ["porbr"] = FmgLanguage.BrazilPortuguese, + ["porpt"] = FmgLanguage.PortugalPortuguese, + ["rusru"] = FmgLanguage.Russian, + ["spaar"] = FmgLanguage.Spanish, + ["spaes"] = FmgLanguage.SpainSpanish, + ["swese"] = FmgLanguage.Swedish, + ["turtr"] = FmgLanguage.Turkish, + ["zhocn"] = FmgLanguage.SimplifiedChinese, + ["zhotw"] = FmgLanguage.TraditionalChinese, + }, + FromLanguageEnum = new() + { + [FmgLanguage.Danish] = "dandk", + [FmgLanguage.German] = "deude", + [FmgLanguage.BritishEnglish] = "enggb", + [FmgLanguage.English] = "engus", + [FmgLanguage.Finnish] = "finfi", + [FmgLanguage.French] = "frafr", + [FmgLanguage.Italian] = "itait", + [FmgLanguage.Japanese] = "jpnjp", + [FmgLanguage.Korean] = "korkr", + [FmgLanguage.Dutch] = "nldnl", + [FmgLanguage.Norwegian] = "norno", + [FmgLanguage.Polish] = "polpl", + [FmgLanguage.BrazilPortuguese] = "porbr", + [FmgLanguage.PortugalPortuguese] = "porpt", + [FmgLanguage.Russian] = "rusru", + [FmgLanguage.Spanish] = "spaar", + [FmgLanguage.SpainSpanish] = "spaes", + [FmgLanguage.Swedish] = "swese", + [FmgLanguage.Turkish] = "turtr", + [FmgLanguage.SimplifiedChinese] = "zhocn", + [FmgLanguage.TraditionalChinese] = "zhotw", + }, + }, + [FromSoftGame.DarkSouls3] = new FmgGameInfo + { + ByType = new() + { + [FmgType.AccessoryCaption] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.AccessoryCaption, FmgType.AccessoryCaption, "アクセサリうんちく", 27), + [FmgType.AccessoryCaption_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.AccessoryCaption_DLC1, FmgType.AccessoryCaption, "アクセサリうんちく_dlc1", 224), + [FmgType.AccessoryCaption_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.AccessoryCaption_DLC2, FmgType.AccessoryCaption, "アクセサリうんちく_dlc2", 264), + [FmgType.AccessoryInfo] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.AccessoryInfo, FmgType.AccessoryInfo, "アクセサリ説明", 23), + [FmgType.AccessoryInfo_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.AccessoryInfo_DLC1, FmgType.AccessoryInfo, "アクセサリ説明_dlc1", 220), + [FmgType.AccessoryInfo_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.AccessoryInfo_DLC2, FmgType.AccessoryInfo, "アクセサリ説明_dlc2", 260), + [FmgType.AccessoryName] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.AccessoryName, FmgType.AccessoryName, "アクセサリ名", 13), + [FmgType.AccessoryName_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.AccessoryName_DLC1, FmgType.AccessoryName, "アクセサリ名_dlc1", 213), + [FmgType.AccessoryName_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.AccessoryName_DLC2, FmgType.AccessoryName, "アクセサリ名_dlc2", 253), + [FmgType.BloodMsg] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.BloodMsg, FmgType.BloodMsg, "血文字", 2), + [FmgType.BloodMsg_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.BloodMsg_DLC1, FmgType.BloodMsg, "血文字_dlc1", 239), + [FmgType.BloodMsg_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.BloodMsg_DLC2, FmgType.BloodMsg, "血文字_dlc2", 279), + [FmgType.Dialogues] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.Dialogues, FmgType.Dialogues, "ダイアログ", 78), + [FmgType.EventText] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.EventText, FmgType.EventText, "イベントテキスト", 30), + [FmgType.EventText_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.EventText_DLC1, FmgType.EventText, "イベントテキスト_dlc1", 231), + [FmgType.EventText_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.EventText_DLC2, FmgType.EventText, "イベントテキスト_dlc2", 271), + [FmgType.GameDialogues] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameDialogues, FmgType.GameDialogues, "FDP_ダイアログ", 204), + [FmgType.GameDialogues_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameDialogues_DLC1, FmgType.GameDialogues, "FDP_ダイアログ_dlc1", 236), + [FmgType.GameDialogues_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameDialogues_DLC2, FmgType.GameDialogues, "FDP_ダイアログ_dlc2", 276), + [FmgType.GameKeyGuide] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameKeyGuide, FmgType.GameKeyGuide, "FDP_キーガイド", 202), + [FmgType.GameLineHelp] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameLineHelp, FmgType.GameLineHelp, "FDP_一行ヘルプ", 201), + [FmgType.GameLineHelp_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameLineHelp_DLC1, FmgType.GameLineHelp, "FDP_一行ヘルプ_dlc1", 233), + [FmgType.GameLineHelp_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameLineHelp_DLC2, FmgType.GameLineHelp, "FDP_一行ヘルプ_dlc2", 273), + [FmgType.GameMenuText] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameMenuText, FmgType.GameMenuText, "FDP_メニューテキスト", 200), + [FmgType.GameMenuText_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameMenuText_DLC1, FmgType.GameMenuText, "FDP_メニューテキスト_dlc1", 232), + [FmgType.GameMenuText_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameMenuText_DLC2, FmgType.GameMenuText, "FDP_メニューテキスト_dlc2", 272), + [FmgType.GameSystemMessagePS4] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameSystemMessagePS4, FmgType.GameSystemMessagePS4, "FDP_システムメッセージ_ps4", 205), + [FmgType.GameSystemMessagePS4_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameSystemMessagePS4_DLC1, FmgType.GameSystemMessagePS4, "FDP_システムメッセージ_ps4_dlc1", 237), + [FmgType.GameSystemMessagePS4_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameSystemMessagePS4_DLC2, FmgType.GameSystemMessagePS4, "FDP_システムメッセージ_ps4_dlc2", 277), + [FmgType.GameSystemMessageWindows] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameSystemMessageWindows, FmgType.GameSystemMessageWindows, "FDP_システムメッセージ_win64", 203), + [FmgType.GameSystemMessageWindows_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameSystemMessageWindows_DLC1, FmgType.GameSystemMessageWindows, "FDP_システムメッセージ_win64_dlc1", 235), + [FmgType.GameSystemMessageWindows_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameSystemMessageWindows_DLC2, FmgType.GameSystemMessageWindows, "FDP_システムメッセージ_win64_dlc2", 275), + [FmgType.GameSystemMessageXboxOne] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameSystemMessageXboxOne, FmgType.GameSystemMessageXboxOne, "FDP_システムメッセージ_xboxone", 206), + [FmgType.GameSystemMessageXboxOne_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameSystemMessageXboxOne_DLC1, FmgType.GameSystemMessageXboxOne, "FDP_システムメッセージ_xboxone_dlc1", 238), + [FmgType.GameSystemMessageXboxOne_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.GameSystemMessageXboxOne_DLC2, FmgType.GameSystemMessageXboxOne, "FDP_システムメッセージ_xboxone_dlc2", 278), + [FmgType.GoodsCaption] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.GoodsCaption, FmgType.GoodsCaption, "アイテムうんちく", 24), + [FmgType.GoodsCaption_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.GoodsCaption_DLC1, FmgType.GoodsCaption, "アイテムうんちく_dlc1", 221), + [FmgType.GoodsCaption_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.GoodsCaption_DLC2, FmgType.GoodsCaption, "アイテムうんちく_dlc2", 261), + [FmgType.GoodsInfo] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.GoodsInfo, FmgType.GoodsInfo, "アイテム説明", 20), + [FmgType.GoodsInfo_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.GoodsInfo_DLC1, FmgType.GoodsInfo, "アイテム説明_dlc1", 217), + [FmgType.GoodsInfo_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.GoodsInfo_DLC2, FmgType.GoodsInfo, "アイテム説明_dlc2", 257), + [FmgType.GoodsName] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.GoodsName, FmgType.GoodsName, "アイテム名", 10), + [FmgType.GoodsName_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.GoodsName_DLC1, FmgType.GoodsName, "アイテム名_dlc1", 210), + [FmgType.GoodsName_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.GoodsName_DLC2, FmgType.GoodsName, "アイテム名_dlc2", 250), + [FmgType.InGameMenu] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.InGameMenu, FmgType.InGameMenu, "インゲームメニュー", 70), + [FmgType.KeyGuide] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.KeyGuide, FmgType.KeyGuide, "キーガイド", 79), + [FmgType.LineHelp] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.LineHelp, FmgType.LineHelp, "一行ヘルプ", 80), + [FmgType.MagicCaption] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.MagicCaption, FmgType.MagicCaption, "魔法うんちく", 29), + [FmgType.MagicCaption_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.MagicCaption_DLC1, FmgType.MagicCaption, "魔法うんちく_dlc1", 226), + [FmgType.MagicCaption_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.MagicCaption_DLC2, FmgType.MagicCaption, "魔法うんちく_dlc2", 266), + [FmgType.MagicInfo] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.MagicInfo, FmgType.MagicInfo, "魔法説明", 28), + [FmgType.MagicInfo_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.MagicInfo_DLC1, FmgType.MagicInfo, "魔法説明_dlc1", 225), + [FmgType.MagicInfo_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.MagicInfo_DLC2, FmgType.MagicInfo, "魔法説明_dlc2", 265), + [FmgType.MagicName] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.MagicName, FmgType.MagicName, "魔法名", 14), + [FmgType.MagicName_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.MagicName_DLC1, FmgType.MagicName, "魔法名_dlc1", 214), + [FmgType.MagicName_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.MagicName_DLC2, FmgType.MagicName, "魔法名_dlc2", 254), + [FmgType.MenuContext] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.MenuContext, FmgType.MenuContext, "項目ヘルプ", 81), + [FmgType.MenuGeneralText] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.MenuGeneralText, FmgType.MenuGeneralText, "メニュー共通テキスト", 76), + [FmgType.MenuOther] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.MenuOther, FmgType.MenuOther, "メニューその他", 77), + [FmgType.MovieSubtitle] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.MovieSubtitle, FmgType.MovieSubtitle, "ムービー字幕", 3), + [FmgType.NpcName] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.NpcName, FmgType.NpcName, "NPC名", 18), + [FmgType.NpcName_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.NpcName_DLC1, FmgType.NpcName, "NPC名_dlc1", 215), + [FmgType.NpcName_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.NpcName_DLC2, FmgType.NpcName, "NPC名_dlc2", 255), + [FmgType.PlaceName] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.PlaceName, FmgType.PlaceName, "地名", 19), + [FmgType.PlaceName_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.PlaceName_DLC1, FmgType.PlaceName, "地名_dlc1", 216), + [FmgType.PlaceName_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.PlaceName_DLC2, FmgType.PlaceName, "地名_dlc2", 256), + [FmgType.ProtectorCaption] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.ProtectorCaption, FmgType.ProtectorCaption, "防具うんちく", 26), + [FmgType.ProtectorCaption_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.ProtectorCaption_DLC1, FmgType.ProtectorCaption, "防具うんちく_dlc1", 223), + [FmgType.ProtectorCaption_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.ProtectorCaption_DLC2, FmgType.ProtectorCaption, "防具うんちく_dlc2", 263), + [FmgType.ProtectorInfo] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.ProtectorInfo, FmgType.ProtectorInfo, "防具説明", 22), + [FmgType.ProtectorName] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.ProtectorName, FmgType.ProtectorName, "防具名", 12), + [FmgType.ProtectorName_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.ProtectorName_DLC1, FmgType.ProtectorName, "防具名_dlc1", 212), + [FmgType.ProtectorName_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.ProtectorName_DLC2, FmgType.ProtectorName, "防具名_dlc2", 252), + [FmgType.Skills] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.Skills, FmgType.Skills, "戦技種別", 40), + [FmgType.TalkMsg] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.TalkMsg, FmgType.TalkMsg, "会話", 1), + [FmgType.TalkMsg_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.TalkMsg_DLC1, FmgType.TalkMsg, "会話_dlc1", 230), + [FmgType.TalkMsg_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.TalkMsg_DLC2, FmgType.TalkMsg, "会話_dlc2", 270), + [FmgType.TextDisplayTagList] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Menu, FmgType.TextDisplayTagList, FmgType.TextDisplayTagList, "テキスト表示用タグ一覧", 90), + [FmgType.WeaponCaption] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.WeaponCaption, FmgType.WeaponCaption, "武器うんちく", 25), + [FmgType.WeaponCaption_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.WeaponCaption_DLC1, FmgType.WeaponCaption, "武器うんちく_dlc1", 222), + [FmgType.WeaponCaption_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.WeaponCaption_DLC2, FmgType.WeaponCaption, "武器うんちく_dlc2", 262), + [FmgType.WeaponInfo] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.WeaponInfo, FmgType.WeaponInfo, "武器説明", 21), + [FmgType.WeaponName] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.WeaponName, FmgType.WeaponName, "武器名", 11), + [FmgType.WeaponName_DLC1] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.WeaponName_DLC1, FmgType.WeaponName, "武器名_dlc1", 211), + [FmgType.WeaponName_DLC2] = new FmgKeyInfo(FromSoftGame.DarkSouls3, FmgCategory.Item, FmgType.WeaponName_DLC2, FmgType.WeaponName, "武器名_dlc2", 251), + }, + Overrides = new() + { + [FmgType.AccessoryCaption] = new List { FmgType.AccessoryCaption_DLC2, FmgType.AccessoryCaption_DLC1 }, + [FmgType.AccessoryInfo] = new List { FmgType.AccessoryInfo_DLC2, FmgType.AccessoryInfo_DLC1 }, + [FmgType.AccessoryName] = new List { FmgType.AccessoryName_DLC2, FmgType.AccessoryName_DLC1 }, + [FmgType.BloodMsg] = new List { FmgType.BloodMsg_DLC2, FmgType.BloodMsg_DLC1 }, + [FmgType.EventText] = new List { FmgType.EventText_DLC2, FmgType.EventText_DLC1 }, + [FmgType.GameDialogues] = new List { FmgType.GameDialogues_DLC2, FmgType.GameDialogues_DLC1 }, + [FmgType.GameLineHelp] = new List { FmgType.GameLineHelp_DLC2, FmgType.GameLineHelp_DLC1 }, + [FmgType.GameMenuText] = new List { FmgType.GameMenuText_DLC2, FmgType.GameMenuText_DLC1 }, + [FmgType.GameSystemMessagePS4] = new List { FmgType.GameSystemMessagePS4_DLC2, FmgType.GameSystemMessagePS4_DLC1 }, + [FmgType.GameSystemMessageWindows] = new List { FmgType.GameSystemMessageWindows_DLC2, FmgType.GameSystemMessageWindows_DLC1 }, + [FmgType.GameSystemMessageXboxOne] = new List { FmgType.GameSystemMessageXboxOne_DLC2, FmgType.GameSystemMessageXboxOne_DLC1 }, + [FmgType.GoodsCaption] = new List { FmgType.GoodsCaption_DLC2, FmgType.GoodsCaption_DLC1 }, + [FmgType.GoodsInfo] = new List { FmgType.GoodsInfo_DLC2, FmgType.GoodsInfo_DLC1 }, + [FmgType.GoodsName] = new List { FmgType.GoodsName_DLC2, FmgType.GoodsName_DLC1 }, + [FmgType.MagicCaption] = new List { FmgType.MagicCaption_DLC2, FmgType.MagicCaption_DLC1 }, + [FmgType.MagicInfo] = new List { FmgType.MagicInfo_DLC2, FmgType.MagicInfo_DLC1 }, + [FmgType.MagicName] = new List { FmgType.MagicName_DLC2, FmgType.MagicName_DLC1 }, + [FmgType.NpcName] = new List { FmgType.NpcName_DLC2, FmgType.NpcName_DLC1 }, + [FmgType.PlaceName] = new List { FmgType.PlaceName_DLC2, FmgType.PlaceName_DLC1 }, + [FmgType.ProtectorCaption] = new List { FmgType.ProtectorCaption_DLC2, FmgType.ProtectorCaption_DLC1 }, + [FmgType.ProtectorName] = new List { FmgType.ProtectorName_DLC2, FmgType.ProtectorName_DLC1 }, + [FmgType.TalkMsg] = new List { FmgType.TalkMsg_DLC2, FmgType.TalkMsg_DLC1 }, + [FmgType.WeaponCaption] = new List { FmgType.WeaponCaption_DLC2, FmgType.WeaponCaption_DLC1 }, + [FmgType.WeaponName] = new List { FmgType.WeaponName_DLC2, FmgType.WeaponName_DLC1 }, + }, + ByFmgName = new() + { + ["FDP_キーガイド"] = new List { FmgType.GameKeyGuide }, + ["FDP_システムメッセージ_ps4_dlc1"] = new List { FmgType.GameSystemMessagePS4_DLC1 }, + ["FDP_システムメッセージ_ps4_dlc2"] = new List { FmgType.GameSystemMessagePS4_DLC2 }, + ["FDP_システムメッセージ_ps4"] = new List { FmgType.GameSystemMessagePS4 }, + ["FDP_システムメッセージ_win64_dlc1"] = new List { FmgType.GameSystemMessageWindows_DLC1 }, + ["FDP_システムメッセージ_win64_dlc2"] = new List { FmgType.GameSystemMessageWindows_DLC2 }, + ["FDP_システムメッセージ_win64"] = new List { FmgType.GameSystemMessageWindows }, + ["FDP_システムメッセージ_xboxone_dlc1"] = new List { FmgType.GameSystemMessageXboxOne_DLC1 }, + ["FDP_システムメッセージ_xboxone_dlc2"] = new List { FmgType.GameSystemMessageXboxOne_DLC2 }, + ["FDP_システムメッセージ_xboxone"] = new List { FmgType.GameSystemMessageXboxOne }, + ["FDP_ダイアログ_dlc1"] = new List { FmgType.GameDialogues_DLC1 }, + ["FDP_ダイアログ_dlc2"] = new List { FmgType.GameDialogues_DLC2 }, + ["FDP_ダイアログ"] = new List { FmgType.GameDialogues }, + ["FDP_メニューテキスト_dlc1"] = new List { FmgType.GameMenuText_DLC1 }, + ["FDP_メニューテキスト_dlc2"] = new List { FmgType.GameMenuText_DLC2 }, + ["FDP_メニューテキスト"] = new List { FmgType.GameMenuText }, + ["FDP_一行ヘルプ_dlc1"] = new List { FmgType.GameLineHelp_DLC1 }, + ["FDP_一行ヘルプ_dlc2"] = new List { FmgType.GameLineHelp_DLC2 }, + ["FDP_一行ヘルプ"] = new List { FmgType.GameLineHelp }, + ["NPC名_dlc1"] = new List { FmgType.NpcName_DLC1 }, + ["NPC名_dlc2"] = new List { FmgType.NpcName_DLC2 }, + ["NPC名"] = new List { FmgType.NpcName }, + ["アイテムうんちく_dlc1"] = new List { FmgType.GoodsCaption_DLC1 }, + ["アイテムうんちく_dlc2"] = new List { FmgType.GoodsCaption_DLC2 }, + ["アイテムうんちく"] = new List { FmgType.GoodsCaption }, + ["アイテム名_dlc1"] = new List { FmgType.GoodsName_DLC1 }, + ["アイテム名_dlc2"] = new List { FmgType.GoodsName_DLC2 }, + ["アイテム名"] = new List { FmgType.GoodsName }, + ["アイテム説明_dlc1"] = new List { FmgType.GoodsInfo_DLC1 }, + ["アイテム説明_dlc2"] = new List { FmgType.GoodsInfo_DLC2 }, + ["アイテム説明"] = new List { FmgType.GoodsInfo }, + ["アクセサリうんちく_dlc1"] = new List { FmgType.AccessoryCaption_DLC1 }, + ["アクセサリうんちく_dlc2"] = new List { FmgType.AccessoryCaption_DLC2 }, + ["アクセサリうんちく"] = new List { FmgType.AccessoryCaption }, + ["アクセサリ名_dlc1"] = new List { FmgType.AccessoryName_DLC1 }, + ["アクセサリ名_dlc2"] = new List { FmgType.AccessoryName_DLC2 }, + ["アクセサリ名"] = new List { FmgType.AccessoryName }, + ["アクセサリ説明_dlc1"] = new List { FmgType.AccessoryInfo_DLC1 }, + ["アクセサリ説明_dlc2"] = new List { FmgType.AccessoryInfo_DLC2 }, + ["アクセサリ説明"] = new List { FmgType.AccessoryInfo }, + ["イベントテキスト_dlc1"] = new List { FmgType.EventText_DLC1 }, + ["イベントテキスト_dlc2"] = new List { FmgType.EventText_DLC2 }, + ["イベントテキスト"] = new List { FmgType.EventText }, + ["インゲームメニュー"] = new List { FmgType.InGameMenu }, + ["キーガイド"] = new List { FmgType.KeyGuide }, + ["ダイアログ"] = new List { FmgType.Dialogues }, + ["テキスト表示用タグ一覧"] = new List { FmgType.TextDisplayTagList }, + ["ムービー字幕"] = new List { FmgType.MovieSubtitle }, + ["メニューその他"] = new List { FmgType.MenuOther }, + ["メニュー共通テキスト"] = new List { FmgType.MenuGeneralText }, + ["一行ヘルプ"] = new List { FmgType.LineHelp }, + ["会話_dlc1"] = new List { FmgType.TalkMsg_DLC1 }, + ["会話_dlc2"] = new List { FmgType.TalkMsg_DLC2 }, + ["会話"] = new List { FmgType.TalkMsg }, + ["地名_dlc1"] = new List { FmgType.PlaceName_DLC1 }, + ["地名_dlc2"] = new List { FmgType.PlaceName_DLC2 }, + ["地名"] = new List { FmgType.PlaceName }, + ["戦技種別"] = new List { FmgType.Skills }, + ["武器うんちく_dlc1"] = new List { FmgType.WeaponCaption_DLC1 }, + ["武器うんちく_dlc2"] = new List { FmgType.WeaponCaption_DLC2 }, + ["武器うんちく"] = new List { FmgType.WeaponCaption }, + ["武器名_dlc1"] = new List { FmgType.WeaponName_DLC1 }, + ["武器名_dlc2"] = new List { FmgType.WeaponName_DLC2 }, + ["武器名"] = new List { FmgType.WeaponName }, + ["武器説明"] = new List { FmgType.WeaponInfo }, + ["血文字_dlc1"] = new List { FmgType.BloodMsg_DLC1 }, + ["血文字_dlc2"] = new List { FmgType.BloodMsg_DLC2 }, + ["血文字"] = new List { FmgType.BloodMsg }, + ["防具うんちく_dlc1"] = new List { FmgType.ProtectorCaption_DLC1 }, + ["防具うんちく_dlc2"] = new List { FmgType.ProtectorCaption_DLC2 }, + ["防具うんちく"] = new List { FmgType.ProtectorCaption }, + ["防具名_dlc1"] = new List { FmgType.ProtectorName_DLC1 }, + ["防具名_dlc2"] = new List { FmgType.ProtectorName_DLC2 }, + ["防具名"] = new List { FmgType.ProtectorName }, + ["防具説明"] = new List { FmgType.ProtectorInfo }, + ["項目ヘルプ"] = new List { FmgType.MenuContext }, + ["魔法うんちく_dlc1"] = new List { FmgType.MagicCaption_DLC1 }, + ["魔法うんちく_dlc2"] = new List { FmgType.MagicCaption_DLC2 }, + ["魔法うんちく"] = new List { FmgType.MagicCaption }, + ["魔法名_dlc1"] = new List { FmgType.MagicName_DLC1 }, + ["魔法名_dlc2"] = new List { FmgType.MagicName_DLC2 }, + ["魔法名"] = new List { FmgType.MagicName }, + ["魔法説明_dlc1"] = new List { FmgType.MagicInfo_DLC1 }, + ["魔法説明_dlc2"] = new List { FmgType.MagicInfo_DLC2 }, + ["魔法説明"] = new List { FmgType.MagicInfo }, + }, + ByBinderID = new() + { + [1] = new List { FmgType.TalkMsg }, + [2] = new List { FmgType.BloodMsg }, + [3] = new List { FmgType.MovieSubtitle }, + [10] = new List { FmgType.GoodsName }, + [11] = new List { FmgType.WeaponName }, + [12] = new List { FmgType.ProtectorName }, + [13] = new List { FmgType.AccessoryName }, + [14] = new List { FmgType.MagicName }, + [18] = new List { FmgType.NpcName }, + [19] = new List { FmgType.PlaceName }, + [20] = new List { FmgType.GoodsInfo }, + [21] = new List { FmgType.WeaponInfo }, + [22] = new List { FmgType.ProtectorInfo }, + [23] = new List { FmgType.AccessoryInfo }, + [24] = new List { FmgType.GoodsCaption }, + [25] = new List { FmgType.WeaponCaption }, + [26] = new List { FmgType.ProtectorCaption }, + [27] = new List { FmgType.AccessoryCaption }, + [28] = new List { FmgType.MagicInfo }, + [29] = new List { FmgType.MagicCaption }, + [30] = new List { FmgType.EventText }, + [40] = new List { FmgType.Skills }, + [70] = new List { FmgType.InGameMenu }, + [76] = new List { FmgType.MenuGeneralText }, + [77] = new List { FmgType.MenuOther }, + [78] = new List { FmgType.Dialogues }, + [79] = new List { FmgType.KeyGuide }, + [80] = new List { FmgType.LineHelp }, + [81] = new List { FmgType.MenuContext }, + [90] = new List { FmgType.TextDisplayTagList }, + [200] = new List { FmgType.GameMenuText }, + [201] = new List { FmgType.GameLineHelp }, + [202] = new List { FmgType.GameKeyGuide }, + [203] = new List { FmgType.GameSystemMessageWindows }, + [204] = new List { FmgType.GameDialogues }, + [205] = new List { FmgType.GameSystemMessagePS4 }, + [206] = new List { FmgType.GameSystemMessageXboxOne }, + [210] = new List { FmgType.GoodsName_DLC1 }, + [211] = new List { FmgType.WeaponName_DLC1 }, + [212] = new List { FmgType.ProtectorName_DLC1 }, + [213] = new List { FmgType.AccessoryName_DLC1 }, + [214] = new List { FmgType.MagicName_DLC1 }, + [215] = new List { FmgType.NpcName_DLC1 }, + [216] = new List { FmgType.PlaceName_DLC1 }, + [217] = new List { FmgType.GoodsInfo_DLC1 }, + [220] = new List { FmgType.AccessoryInfo_DLC1 }, + [221] = new List { FmgType.GoodsCaption_DLC1 }, + [222] = new List { FmgType.WeaponCaption_DLC1 }, + [223] = new List { FmgType.ProtectorCaption_DLC1 }, + [224] = new List { FmgType.AccessoryCaption_DLC1 }, + [225] = new List { FmgType.MagicInfo_DLC1 }, + [226] = new List { FmgType.MagicCaption_DLC1 }, + [230] = new List { FmgType.TalkMsg_DLC1 }, + [231] = new List { FmgType.EventText_DLC1 }, + [232] = new List { FmgType.GameMenuText_DLC1 }, + [233] = new List { FmgType.GameLineHelp_DLC1 }, + [235] = new List { FmgType.GameSystemMessageWindows_DLC1 }, + [236] = new List { FmgType.GameDialogues_DLC1 }, + [237] = new List { FmgType.GameSystemMessagePS4_DLC1 }, + [238] = new List { FmgType.GameSystemMessageXboxOne_DLC1 }, + [239] = new List { FmgType.BloodMsg_DLC1 }, + [250] = new List { FmgType.GoodsName_DLC2 }, + [251] = new List { FmgType.WeaponName_DLC2 }, + [252] = new List { FmgType.ProtectorName_DLC2 }, + [253] = new List { FmgType.AccessoryName_DLC2 }, + [254] = new List { FmgType.MagicName_DLC2 }, + [255] = new List { FmgType.NpcName_DLC2 }, + [256] = new List { FmgType.PlaceName_DLC2 }, + [257] = new List { FmgType.GoodsInfo_DLC2 }, + [260] = new List { FmgType.AccessoryInfo_DLC2 }, + [261] = new List { FmgType.GoodsCaption_DLC2 }, + [262] = new List { FmgType.WeaponCaption_DLC2 }, + [263] = new List { FmgType.ProtectorCaption_DLC2 }, + [264] = new List { FmgType.AccessoryCaption_DLC2 }, + [265] = new List { FmgType.MagicInfo_DLC2 }, + [266] = new List { FmgType.MagicCaption_DLC2 }, + [270] = new List { FmgType.TalkMsg_DLC2 }, + [271] = new List { FmgType.EventText_DLC2 }, + [272] = new List { FmgType.GameMenuText_DLC2 }, + [273] = new List { FmgType.GameLineHelp_DLC2 }, + [275] = new List { FmgType.GameSystemMessageWindows_DLC2 }, + [276] = new List { FmgType.GameDialogues_DLC2 }, + [277] = new List { FmgType.GameSystemMessagePS4_DLC2 }, + [278] = new List { FmgType.GameSystemMessageXboxOne_DLC2 }, + [279] = new List { FmgType.BloodMsg_DLC2 }, + }, + ToLanguageEnum = new() + { + ["deude"] = FmgLanguage.German, + ["engus"] = FmgLanguage.English, + ["frafr"] = FmgLanguage.French, + ["itait"] = FmgLanguage.Italian, + ["jpnjp"] = FmgLanguage.Japanese, + ["korkr"] = FmgLanguage.Korean, + ["polpl"] = FmgLanguage.Polish, + ["porbr"] = FmgLanguage.BrazilPortuguese, + ["rusru"] = FmgLanguage.Russian, + ["spaar"] = FmgLanguage.Spanish, + ["spaes"] = FmgLanguage.SpainSpanish, + ["zhocn"] = FmgLanguage.SimplifiedChinese, + ["zhotw"] = FmgLanguage.TraditionalChinese, + }, + FromLanguageEnum = new() + { + [FmgLanguage.German] = "deude", + [FmgLanguage.English] = "engus", + [FmgLanguage.French] = "frafr", + [FmgLanguage.Italian] = "itait", + [FmgLanguage.Japanese] = "jpnjp", + [FmgLanguage.Korean] = "korkr", + [FmgLanguage.Polish] = "polpl", + [FmgLanguage.BrazilPortuguese] = "porbr", + [FmgLanguage.Russian] = "rusru", + [FmgLanguage.Spanish] = "spaar", + [FmgLanguage.SpainSpanish] = "spaes", + [FmgLanguage.SimplifiedChinese] = "zhocn", + [FmgLanguage.TraditionalChinese] = "zhotw", + }, + }, + [FromSoftGame.Sekiro] = new FmgGameInfo + { + ByType = new() + { + [FmgType.AccessoryCaption] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.AccessoryCaption, FmgType.AccessoryCaption, "アクセサリうんちく", 27), + [FmgType.AccessoryInfo] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.AccessoryInfo, FmgType.AccessoryInfo, "アクセサリ説明", 23), + [FmgType.AccessoryName] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.AccessoryName, FmgType.AccessoryName, "アクセサリ名", 13), + [FmgType.BloodMsg] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.BloodMsg, FmgType.BloodMsg, "血文字", 2), + [FmgType.Dialogues] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.Dialogues, FmgType.Dialogues, "ダイアログ", 78), + [FmgType.EventText] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.EventText, FmgType.EventText, "イベントテキスト", 30), + [FmgType.GameDialogues] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.GameDialogues, FmgType.GameDialogues, "NTC_ダイアログ", 204), + [FmgType.GameKeyGuide] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.GameKeyGuide, FmgType.GameKeyGuide, "NTC_キーガイド", 202), + [FmgType.GameLineHelp] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.GameLineHelp, FmgType.GameLineHelp, "NTC_一行ヘルプ", 201), + [FmgType.GameMenuText] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.GameMenuText, FmgType.GameMenuText, "NTC_メニューテキスト", 200), + [FmgType.GameSystemMessageWindows] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.GameSystemMessageWindows, FmgType.GameSystemMessageWindows, "NTC_システムメッセージ_win64", 203), + [FmgType.GoodsCaption] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.GoodsCaption, FmgType.GoodsCaption, "アイテムうんちく", 24), + [FmgType.GoodsInfo] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.GoodsInfo, FmgType.GoodsInfo, "アイテム説明", 20), + [FmgType.GoodsName] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.GoodsName, FmgType.GoodsName, "アイテム名", 10), + [FmgType.InGameMenu] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.InGameMenu, FmgType.InGameMenu, "インゲームメニュー", 70), + [FmgType.KeyGuide] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.KeyGuide, FmgType.KeyGuide, "キーガイド", 79), + [FmgType.LineHelp] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.LineHelp, FmgType.LineHelp, "一行ヘルプ", 80), + [FmgType.LoadingText] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.LoadingText, FmgType.LoadingText, "ローディングテキスト", 205), + [FmgType.LoadingTitle] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.LoadingTitle, FmgType.LoadingTitle, "ローディングタイトル", 206), + [FmgType.MagicCaption] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.MagicCaption, FmgType.MagicCaption, "魔法うんちく", 29), + [FmgType.MagicInfo] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.MagicInfo, FmgType.MagicInfo, "魔法説明", 28), + [FmgType.MagicName] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.MagicName, FmgType.MagicName, "魔法名", 14), + [FmgType.MenuContext] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.MenuContext, FmgType.MenuContext, "項目ヘルプ", 81), + [FmgType.MenuGeneralText] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.MenuGeneralText, FmgType.MenuGeneralText, "メニュー共通テキスト", 76), + [FmgType.MenuOther] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.MenuOther, FmgType.MenuOther, "メニューその他", 77), + [FmgType.MovieSubtitle] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.MovieSubtitle, FmgType.MovieSubtitle, "ムービー字幕", 3), + [FmgType.NpcName] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.NpcName, FmgType.NpcName, "NPC名", 18), + [FmgType.PlaceName] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.PlaceName, FmgType.PlaceName, "地名", 19), + [FmgType.ProtectorCaption] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.ProtectorCaption, FmgType.ProtectorCaption, "防具うんちく", 26), + [FmgType.ProtectorInfo] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.ProtectorInfo, FmgType.ProtectorInfo, "防具説明", 22), + [FmgType.ProtectorName] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.ProtectorName, FmgType.ProtectorName, "防具名", 12), + [FmgType.Skills] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.Skills, FmgType.Skills, "戦技種別", 40), + [FmgType.TalkMsg] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.TalkMsg, FmgType.TalkMsg, "会話", 1), + [FmgType.TextDisplayTagList] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Menu, FmgType.TextDisplayTagList, FmgType.TextDisplayTagList, "テキスト表示用タグ一覧", 90), + [FmgType.WeaponCaption] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.WeaponCaption, FmgType.WeaponCaption, "武器うんちく", 25), + [FmgType.WeaponInfo] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.WeaponInfo, FmgType.WeaponInfo, "武器説明", 21), + [FmgType.WeaponName] = new FmgKeyInfo(FromSoftGame.Sekiro, FmgCategory.Item, FmgType.WeaponName, FmgType.WeaponName, "武器名", 11), + }, + Overrides = new() + { + + }, + ByFmgName = new() + { + ["NPC名"] = new List { FmgType.NpcName }, + ["NTC_キーガイド"] = new List { FmgType.GameKeyGuide }, + ["NTC_システムメッセージ_win64"] = new List { FmgType.GameSystemMessageWindows }, + ["NTC_ダイアログ"] = new List { FmgType.GameDialogues }, + ["NTC_メニューテキスト"] = new List { FmgType.GameMenuText }, + ["NTC_一行ヘルプ"] = new List { FmgType.GameLineHelp }, + ["アイテムうんちく"] = new List { FmgType.GoodsCaption }, + ["アイテム名"] = new List { FmgType.GoodsName }, + ["アイテム説明"] = new List { FmgType.GoodsInfo }, + ["アクセサリうんちく"] = new List { FmgType.AccessoryCaption }, + ["アクセサリ名"] = new List { FmgType.AccessoryName }, + ["アクセサリ説明"] = new List { FmgType.AccessoryInfo }, + ["イベントテキスト"] = new List { FmgType.EventText }, + ["インゲームメニュー"] = new List { FmgType.InGameMenu }, + ["キーガイド"] = new List { FmgType.KeyGuide }, + ["ダイアログ"] = new List { FmgType.Dialogues }, + ["テキスト表示用タグ一覧"] = new List { FmgType.TextDisplayTagList }, + ["ムービー字幕"] = new List { FmgType.MovieSubtitle }, + ["メニューその他"] = new List { FmgType.MenuOther }, + ["メニュー共通テキスト"] = new List { FmgType.MenuGeneralText }, + ["ローディングタイトル"] = new List { FmgType.LoadingTitle }, + ["ローディングテキスト"] = new List { FmgType.LoadingText }, + ["一行ヘルプ"] = new List { FmgType.LineHelp }, + ["会話"] = new List { FmgType.TalkMsg }, + ["地名"] = new List { FmgType.PlaceName }, + ["戦技種別"] = new List { FmgType.Skills }, + ["武器うんちく"] = new List { FmgType.WeaponCaption }, + ["武器名"] = new List { FmgType.WeaponName }, + ["武器説明"] = new List { FmgType.WeaponInfo }, + ["血文字"] = new List { FmgType.BloodMsg }, + ["防具うんちく"] = new List { FmgType.ProtectorCaption }, + ["防具名"] = new List { FmgType.ProtectorName }, + ["防具説明"] = new List { FmgType.ProtectorInfo }, + ["項目ヘルプ"] = new List { FmgType.MenuContext }, + ["魔法うんちく"] = new List { FmgType.MagicCaption }, + ["魔法名"] = new List { FmgType.MagicName }, + ["魔法説明"] = new List { FmgType.MagicInfo }, + }, + ByBinderID = new() + { + [1] = new List { FmgType.TalkMsg }, + [2] = new List { FmgType.BloodMsg }, + [3] = new List { FmgType.MovieSubtitle }, + [10] = new List { FmgType.GoodsName }, + [11] = new List { FmgType.WeaponName }, + [12] = new List { FmgType.ProtectorName }, + [13] = new List { FmgType.AccessoryName }, + [14] = new List { FmgType.MagicName }, + [18] = new List { FmgType.NpcName }, + [19] = new List { FmgType.PlaceName }, + [20] = new List { FmgType.GoodsInfo }, + [21] = new List { FmgType.WeaponInfo }, + [22] = new List { FmgType.ProtectorInfo }, + [23] = new List { FmgType.AccessoryInfo }, + [24] = new List { FmgType.GoodsCaption }, + [25] = new List { FmgType.WeaponCaption }, + [26] = new List { FmgType.ProtectorCaption }, + [27] = new List { FmgType.AccessoryCaption }, + [28] = new List { FmgType.MagicInfo }, + [29] = new List { FmgType.MagicCaption }, + [30] = new List { FmgType.EventText }, + [40] = new List { FmgType.Skills }, + [70] = new List { FmgType.InGameMenu }, + [76] = new List { FmgType.MenuGeneralText }, + [77] = new List { FmgType.MenuOther }, + [78] = new List { FmgType.Dialogues }, + [79] = new List { FmgType.KeyGuide }, + [80] = new List { FmgType.LineHelp }, + [81] = new List { FmgType.MenuContext }, + [90] = new List { FmgType.TextDisplayTagList }, + [200] = new List { FmgType.GameMenuText }, + [201] = new List { FmgType.GameLineHelp }, + [202] = new List { FmgType.GameKeyGuide }, + [203] = new List { FmgType.GameSystemMessageWindows }, + [204] = new List { FmgType.GameDialogues }, + [205] = new List { FmgType.LoadingText }, + [206] = new List { FmgType.LoadingTitle }, + }, + ToLanguageEnum = new() + { + ["deude"] = FmgLanguage.German, + ["engus"] = FmgLanguage.English, + ["frafr"] = FmgLanguage.French, + ["itait"] = FmgLanguage.Italian, + ["jpnjp"] = FmgLanguage.Japanese, + ["korkr"] = FmgLanguage.Korean, + ["polpl"] = FmgLanguage.Polish, + ["porbr"] = FmgLanguage.BrazilPortuguese, + ["rusru"] = FmgLanguage.Russian, + ["spaar"] = FmgLanguage.Spanish, + ["spaes"] = FmgLanguage.SpainSpanish, + ["thath"] = FmgLanguage.Thai, + ["zhocn"] = FmgLanguage.SimplifiedChinese, + ["zhotw"] = FmgLanguage.TraditionalChinese, + }, + FromLanguageEnum = new() + { + [FmgLanguage.German] = "deude", + [FmgLanguage.English] = "engus", + [FmgLanguage.French] = "frafr", + [FmgLanguage.Italian] = "itait", + [FmgLanguage.Japanese] = "jpnjp", + [FmgLanguage.Korean] = "korkr", + [FmgLanguage.Polish] = "polpl", + [FmgLanguage.BrazilPortuguese] = "porbr", + [FmgLanguage.Russian] = "rusru", + [FmgLanguage.Spanish] = "spaar", + [FmgLanguage.SpainSpanish] = "spaes", + [FmgLanguage.Thai] = "thath", + [FmgLanguage.SimplifiedChinese] = "zhocn", + [FmgLanguage.TraditionalChinese] = "zhotw", + }, + }, + [FromSoftGame.EldenRing] = new FmgGameInfo + { + ByType = new() + { + [FmgType.AccessoryCaption] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.AccessoryCaption, FmgType.AccessoryCaption, "AccessoryCaption", 27), + [FmgType.AccessoryInfo] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.AccessoryInfo, FmgType.AccessoryInfo, "AccessoryInfo", 23), + [FmgType.AccessoryName] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.AccessoryName, FmgType.AccessoryName, "AccessoryName", 13), + [FmgType.ActionButtonText] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.ActionButtonText, FmgType.ActionButtonText, "ActionButtonText", 32), + [FmgType.ArtsCaption] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.ArtsCaption, FmgType.ArtsCaption, "ArtsCaption", 43), + [FmgType.ArtsName] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.ArtsName, FmgType.ArtsName, "ArtsName", 42), + [FmgType.BloodMsg] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.BloodMsg, FmgType.BloodMsg, "BloodMsg", 2), + [FmgType.EventTextForMap] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.EventTextForMap, FmgType.EventTextForMap, "EventTextForMap", 34), + [FmgType.EventTextForTalk] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.EventTextForTalk, FmgType.EventTextForTalk, "EventTextForTalk", 33), + [FmgType.GameDialogues] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.GameDialogues, FmgType.GameDialogues, "GR_Dialogues", 204), + [FmgType.GameKeyGuide] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.GameKeyGuide, FmgType.GameKeyGuide, "GR_KeyGuide", 202), + [FmgType.GameLineHelp] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.GameLineHelp, FmgType.GameLineHelp, "GR_LineHelp", 201), + [FmgType.GameMenuText] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.GameMenuText, FmgType.GameMenuText, "GR_MenuText", 200), + [FmgType.GameSystemMessageWindows] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.GameSystemMessageWindows, FmgType.GameSystemMessageWindows, "GR_System_Message_win64", 203), + [FmgType.GemCaption] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.GemCaption, FmgType.GemCaption, "GemCaption", 37), + [FmgType.GemEffect] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.GemEffect, FmgType.GemEffect, "GemEffect", 45), + [FmgType.GemInfo] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.GemInfo, FmgType.GemInfo, "GemInfo", 36), + [FmgType.GemName] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.GemName, FmgType.GemName, "GemName", 35), + [FmgType.GoodsCaption] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.GoodsCaption, FmgType.GoodsCaption, "GoodsCaption", 24), + [FmgType.GoodsDialog] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.GoodsDialog, FmgType.GoodsDialog, "GoodsDialog", 41), + [FmgType.GoodsInfo] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.GoodsInfo, FmgType.GoodsInfo, "GoodsInfo", 20), + [FmgType.GoodsInfo2] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.GoodsInfo2, FmgType.GoodsInfo2, "GoodsInfo2", 46), + [FmgType.GoodsName] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.GoodsName, FmgType.GoodsName, "GoodsName", 10), + [FmgType.LoadingText] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.LoadingText, FmgType.LoadingText, "LoadingText", 206), + [FmgType.LoadingTitle] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.LoadingTitle, FmgType.LoadingTitle, "LoadingTitle", 205), + [FmgType.MagicCaption] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.MagicCaption, FmgType.MagicCaption, "MagicCaption", 29), + [FmgType.MagicInfo] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.MagicInfo, FmgType.MagicInfo, "MagicInfo", 28), + [FmgType.MagicName] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.MagicName, FmgType.MagicName, "MagicName", 14), + [FmgType.MovieSubtitle] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.MovieSubtitle, FmgType.MovieSubtitle, "MovieSubtitle", 3), + [FmgType.NetworkMessage] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.NetworkMessage, FmgType.NetworkMessage, "NetworkMessage", 31), + [FmgType.NpcName] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.NpcName, FmgType.NpcName, "NpcName", 18), + [FmgType.PlaceName] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.PlaceName, FmgType.PlaceName, "PlaceName", 19), + [FmgType.ProtectorCaption] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.ProtectorCaption, FmgType.ProtectorCaption, "ProtectorCaption", 26), + [FmgType.ProtectorInfo] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.ProtectorInfo, FmgType.ProtectorInfo, "ProtectorInfo", 22), + [FmgType.ProtectorName] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.ProtectorName, FmgType.ProtectorName, "ProtectorName", 12), + [FmgType.TalkMsg] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.TalkMsg, FmgType.TalkMsg, "TalkMsg", 1), + [FmgType.TalkMsgFemalePCAlt] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.TalkMsgFemalePCAlt, FmgType.TalkMsgFemalePCAlt, "TalkMsg_FemalePC_Alt", 4), + [FmgType.TextEmbedImageNameWindows] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.TextEmbedImageNameWindows, FmgType.TextEmbedImageNameWindows, "TextEmbedImageName_win64", 209), + [FmgType.TosWindows] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.TosWindows, FmgType.TosWindows, "ToS_win64", 210), + [FmgType.TutorialBody] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.TutorialBody, FmgType.TutorialBody, "TutorialBody", 208), + [FmgType.TutorialTitle] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Menu, FmgType.TutorialTitle, FmgType.TutorialTitle, "TutorialTitle", 207), + [FmgType.WeaponCaption] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.WeaponCaption, FmgType.WeaponCaption, "WeaponCaption", 25), + [FmgType.WeaponEffect] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.WeaponEffect, FmgType.WeaponEffect, "WeaponEffect", 44), + [FmgType.WeaponInfo] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.WeaponInfo, FmgType.WeaponInfo, "WeaponInfo", 21), + [FmgType.WeaponName] = new FmgKeyInfo(FromSoftGame.EldenRing, FmgCategory.Item, FmgType.WeaponName, FmgType.WeaponName, "WeaponName", 11), + }, + Overrides = new() + { + + }, + ByFmgName = new() + { + ["AccessoryCaption"] = new List { FmgType.AccessoryCaption }, + ["AccessoryInfo"] = new List { FmgType.AccessoryInfo }, + ["AccessoryName"] = new List { FmgType.AccessoryName }, + ["ActionButtonText"] = new List { FmgType.ActionButtonText }, + ["ArtsCaption"] = new List { FmgType.ArtsCaption }, + ["ArtsName"] = new List { FmgType.ArtsName }, + ["BloodMsg"] = new List { FmgType.BloodMsg }, + ["EventTextForMap"] = new List { FmgType.EventTextForMap }, + ["EventTextForTalk"] = new List { FmgType.EventTextForTalk }, + ["GemCaption"] = new List { FmgType.GemCaption }, + ["GemEffect"] = new List { FmgType.GemEffect }, + ["GemInfo"] = new List { FmgType.GemInfo }, + ["GemName"] = new List { FmgType.GemName }, + ["GoodsCaption"] = new List { FmgType.GoodsCaption }, + ["GoodsDialog"] = new List { FmgType.GoodsDialog }, + ["GoodsInfo"] = new List { FmgType.GoodsInfo }, + ["GoodsInfo2"] = new List { FmgType.GoodsInfo2 }, + ["GoodsName"] = new List { FmgType.GoodsName }, + ["GR_Dialogues"] = new List { FmgType.GameDialogues }, + ["GR_KeyGuide"] = new List { FmgType.GameKeyGuide }, + ["GR_LineHelp"] = new List { FmgType.GameLineHelp }, + ["GR_MenuText"] = new List { FmgType.GameMenuText }, + ["GR_System_Message_win64"] = new List { FmgType.GameSystemMessageWindows }, + ["LoadingText"] = new List { FmgType.LoadingText }, + ["LoadingTitle"] = new List { FmgType.LoadingTitle }, + ["MagicCaption"] = new List { FmgType.MagicCaption }, + ["MagicInfo"] = new List { FmgType.MagicInfo }, + ["MagicName"] = new List { FmgType.MagicName }, + ["MovieSubtitle"] = new List { FmgType.MovieSubtitle }, + ["NetworkMessage"] = new List { FmgType.NetworkMessage }, + ["NpcName"] = new List { FmgType.NpcName }, + ["PlaceName"] = new List { FmgType.PlaceName }, + ["ProtectorCaption"] = new List { FmgType.ProtectorCaption }, + ["ProtectorInfo"] = new List { FmgType.ProtectorInfo }, + ["ProtectorName"] = new List { FmgType.ProtectorName }, + ["TalkMsg_FemalePC_Alt"] = new List { FmgType.TalkMsgFemalePCAlt }, + ["TalkMsg"] = new List { FmgType.TalkMsg }, + ["TextEmbedImageName_win64"] = new List { FmgType.TextEmbedImageNameWindows }, + ["ToS_win64"] = new List { FmgType.TosWindows }, + ["TutorialBody"] = new List { FmgType.TutorialBody }, + ["TutorialTitle"] = new List { FmgType.TutorialTitle }, + ["WeaponCaption"] = new List { FmgType.WeaponCaption }, + ["WeaponEffect"] = new List { FmgType.WeaponEffect }, + ["WeaponInfo"] = new List { FmgType.WeaponInfo }, + ["WeaponName"] = new List { FmgType.WeaponName }, + }, + ByBinderID = new() + { + [1] = new List { FmgType.TalkMsg }, + [2] = new List { FmgType.BloodMsg }, + [3] = new List { FmgType.MovieSubtitle }, + [4] = new List { FmgType.TalkMsgFemalePCAlt }, + [10] = new List { FmgType.GoodsName }, + [11] = new List { FmgType.WeaponName }, + [12] = new List { FmgType.ProtectorName }, + [13] = new List { FmgType.AccessoryName }, + [14] = new List { FmgType.MagicName }, + [18] = new List { FmgType.NpcName }, + [19] = new List { FmgType.PlaceName }, + [20] = new List { FmgType.GoodsInfo }, + [21] = new List { FmgType.WeaponInfo }, + [22] = new List { FmgType.ProtectorInfo }, + [23] = new List { FmgType.AccessoryInfo }, + [24] = new List { FmgType.GoodsCaption }, + [25] = new List { FmgType.WeaponCaption }, + [26] = new List { FmgType.ProtectorCaption }, + [27] = new List { FmgType.AccessoryCaption }, + [28] = new List { FmgType.MagicInfo }, + [29] = new List { FmgType.MagicCaption }, + [31] = new List { FmgType.NetworkMessage }, + [32] = new List { FmgType.ActionButtonText }, + [33] = new List { FmgType.EventTextForTalk }, + [34] = new List { FmgType.EventTextForMap }, + [35] = new List { FmgType.GemName }, + [36] = new List { FmgType.GemInfo }, + [37] = new List { FmgType.GemCaption }, + [41] = new List { FmgType.GoodsDialog }, + [42] = new List { FmgType.ArtsName }, + [43] = new List { FmgType.ArtsCaption }, + [44] = new List { FmgType.WeaponEffect }, + [45] = new List { FmgType.GemEffect }, + [46] = new List { FmgType.GoodsInfo2 }, + [200] = new List { FmgType.GameMenuText }, + [201] = new List { FmgType.GameLineHelp }, + [202] = new List { FmgType.GameKeyGuide }, + [203] = new List { FmgType.GameSystemMessageWindows }, + [204] = new List { FmgType.GameDialogues }, + [205] = new List { FmgType.LoadingTitle }, + [206] = new List { FmgType.LoadingText }, + [207] = new List { FmgType.TutorialTitle }, + [208] = new List { FmgType.TutorialBody }, + [209] = new List { FmgType.TextEmbedImageNameWindows }, + [210] = new List { FmgType.TosWindows }, + }, + ToLanguageEnum = new() + { + ["deude"] = FmgLanguage.German, + ["engus"] = FmgLanguage.English, + ["frafr"] = FmgLanguage.French, + ["itait"] = FmgLanguage.Italian, + ["jpnjp"] = FmgLanguage.Japanese, + ["korkr"] = FmgLanguage.Korean, + ["polpl"] = FmgLanguage.Polish, + ["porbr"] = FmgLanguage.BrazilPortuguese, + ["rusru"] = FmgLanguage.Russian, + ["spaar"] = FmgLanguage.Spanish, + ["spaes"] = FmgLanguage.SpainSpanish, + ["thath"] = FmgLanguage.Thai, + ["zhocn"] = FmgLanguage.SimplifiedChinese, + ["zhotw"] = FmgLanguage.TraditionalChinese, + }, + FromLanguageEnum = new() + { + [FmgLanguage.German] = "deude", + [FmgLanguage.English] = "engus", + [FmgLanguage.French] = "frafr", + [FmgLanguage.Italian] = "itait", + [FmgLanguage.Japanese] = "jpnjp", + [FmgLanguage.Korean] = "korkr", + [FmgLanguage.Polish] = "polpl", + [FmgLanguage.BrazilPortuguese] = "porbr", + [FmgLanguage.Russian] = "rusru", + [FmgLanguage.Spanish] = "spaar", + [FmgLanguage.SpainSpanish] = "spaes", + [FmgLanguage.Thai] = "thath", + [FmgLanguage.SimplifiedChinese] = "zhocn", + [FmgLanguage.TraditionalChinese] = "zhotw", + }, + }, + }; + } +} diff --git a/SoapstoneLib/SoapstoneLib/SoulsFmg.cs b/SoapstoneLib/SoapstoneLib/SoulsFmg.cs new file mode 100644 index 0000000..83e6c3c --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/SoulsFmg.cs @@ -0,0 +1,220 @@ +using System.Collections.Generic; +using System.Linq; +using SoapstoneLib.Proto; + +namespace SoapstoneLib +{ + /// + /// Utility class for identifying all FMGs across all games. + /// + /// No in-game key consistently works across all games, so Soapstone uses a custom FmgType enum + /// which maps to other key data per-game. + /// + public static partial class SoulsFmg + { + /// + /// High-level grouping for a FMG. This can be expanded to represent other groupings. + /// + public enum FmgCategory + { + /// + /// FMG not contained within any msgbnd. + /// + None, + /// + /// FMG contained within an item msgbnd. + /// + Item, + /// + /// FMG contained within a menu msgbnd + /// + Menu, + } + + /// + /// Produces all FmgTypes for a given game and binder ID. + /// + /// When this returns true, the list will contain at least one element. In all + /// currently supported FromSoft games, the list contains exactly one element. + /// + public static bool TryGetFmgBinderType(FromSoftGame game, int binderID, out List types) + { + if (TryGetFmgGameInfo(game, out FmgGameInfo gameInfo) + && gameInfo.ByBinderID.TryGetValue(binderID, out types)) + { + // Defensive copy + types = types.ToList(); + return true; + } + types = null; + return false; + } + + /// + /// Produces all FmgTypes for a given game and vanilla FMG name, without the ".fmg" suffix. + /// + /// When this returns true, the list will contain at least one element. In all + /// currently supported FromSoft games, at most two types may be returned. + /// + public static bool TryGetFmgNameType(FromSoftGame game, string fmgName, out List types) + { + if (TryGetFmgGameInfo(game, out FmgGameInfo gameInfo) + && gameInfo.ByFmgName.TryGetValue(fmgName, out types)) + { + // Defensive copy + types = types.ToList(); + return true; + } + types = null; + return false; + } + + /// + /// Produces FmgKeyInfo metadata for a given game and FmgType. + /// + public static bool TryGetFmgInfo(FromSoftGame game, FmgType type, out FmgKeyInfo info) + { + if (TryGetFmgGameInfo(game, out FmgGameInfo gameInfo) + && gameInfo.ByType.TryGetValue(type, out info)) + { + return true; + } + info = null; + return false; + } + + /// + /// Produces FmgKeyInfo metadata for a given game and key. + /// + public static bool TryGetFmgInfo(FromSoftGame game, SoulsKey.FmgKey key, out FmgKeyInfo info) + { + return TryGetFmgInfo(game, key.Type, out info); + } + + /// + /// Produces a language folder name for a given game and FmgLanguage. + /// + /// This is "japanese" for Demon's Souls Japanese, which does not have a dedicated folder. + /// + public static bool TryGetFmgLanguage(FromSoftGame game, FmgLanguage langEnum, out string lang) + { + if (TryGetFmgGameInfo(game, out FmgGameInfo gameInfo) + && gameInfo.FromLanguageEnum.TryGetValue(langEnum, out lang)) + { + return true; + } + lang = null; + return false; + } + + /// + /// Produces a language folder name for a given game and key. + /// + public static bool TryGetFmgLanguage(FromSoftGame game, SoulsKey.FmgKey key, out string lang) + { + return TryGetFmgLanguage(game, key.Language, out lang); + } + + /// + /// Produces an FmgLanguage for a given game and language folder name. + /// + public static bool TryGetFmgLanguageEnum(FromSoftGame game, string lang, out FmgLanguage langEnum) + { + if (TryGetFmgGameInfo(game, out FmgGameInfo gameInfo) + && gameInfo.ToLanguageEnum.TryGetValue(lang.ToLowerInvariant(), out langEnum)) + { + return true; + } + langEnum = default; + return false; + } + + /// + /// Produces all FMG file keys for a given base type. + /// + /// This is used in cases where patch FMG files for DLC. The keys + /// are returned in decreasing order of bnd id, or decreasing order + /// of priority. + /// + public static bool TryGetBaseFmgKeys( + FromSoftGame game, + FmgType baseType, + FmgLanguage lang, + out List keys) + { + if (!TryGetFmgGameInfo(game, out FmgGameInfo gameInfo) + || !gameInfo.ByType.ContainsKey(baseType) + || !gameInfo.FromLanguageEnum.ContainsKey(lang)) + { + keys = null; + return false; + } + keys = new(); + if (gameInfo.Overrides.TryGetValue(baseType, out List overrides)) + { + foreach (FmgType altType in overrides) + { + keys.Add(new SoulsKey.FmgKey(lang, altType)); + } + } + keys.Add(new SoulsKey.FmgKey(lang, baseType)); + return true; + } + + /// + /// Game-specific info for a known FMG file. + /// + public class FmgKeyInfo + { + internal FmgKeyInfo(FromSoftGame Game, FmgCategory Category, FmgType Type, FmgType BaseType, string FmgName, int BinderID) + { + this.Game = Game; + this.Category = Category; + this.Type = Type; + this.BaseType = BaseType; + this.FmgName = FmgName; + this.BinderID = BinderID; + } + + /// + /// The game where this FMG can be found. + /// + public FromSoftGame Game { get; } + + /// + /// Which FMG grouping this FMG belongs to, including overall binder file. + /// + public FmgCategory Category { get; } + + /// + /// The type designation for this FMG, uniquely identifying it within the game. + /// + public FmgType Type { get; } + + /// + /// The base type designation. For DLC or patch files, this is the main file. Otherwise, this matches Type. + /// + public FmgType BaseType { get; } + + /// + /// The vanilla name of the FMG file, not including the ".fmg" file extension. + /// + public string FmgName { get; } + + /// + /// The binder ID for this FMG, or -1 if it's not in a msgbnd. + /// + public int BinderID { get; } + } + + internal class FmgGameInfo + { + public Dictionary ByType { get; set; } + public Dictionary> Overrides { get; set; } + public Dictionary> ByFmgName { get; set; } + public Dictionary> ByBinderID { get; set; } + public Dictionary ToLanguageEnum { get; set; } + public Dictionary FromLanguageEnum { get; set; } + } + } +} diff --git a/SoapstoneLib/SoapstoneLib/SoulsKey.cs b/SoapstoneLib/SoapstoneLib/SoulsKey.cs new file mode 100644 index 0000000..49eda25 --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/SoulsKey.cs @@ -0,0 +1,411 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using SoapstoneLib.Proto; + +namespace SoapstoneLib +{ + /// + /// A key for an entity in a FromSoft game. + /// + /// The possible instances of this are currently sealed within this class. More can be added here, + /// as well as custom per-editor types not directly representing game data. + /// + public interface SoulsKey + { + /// + /// The game file associated with this entity. + /// + /// In the case of a bnd file, this may be an individual file within the archive. + /// + FileKey File { get; } + + /// + /// Overall key type directly representing a file, like an MSB or FMG file. + /// + public abstract class FileKey : SoulsKey + { + /// + /// Self-reference to this file. + /// + public FileKey File => this; + + private protected abstract string InternalName { get; } + + // Try to reuse string instances when possible to minimize serialization overhead + private string fileName; + internal string FileName + { + get + { + if (fileName == null) + { + fileName = InternalName; + } + return fileName; + } + } + } + + /// + /// Key for a param file representing a gameparam, such as SpEffectParam. + /// + public sealed class GameParamKey : FileKey + { + /// + /// Key type which can be used to request and return this type in entity search RPCs. + /// + public static SoulsKeyType KeyType { get; } = + SoulsKeyType.ForFile("param/GameParam/{0}.param", typeof(GameParamKey)); + + /// + /// Creates a key with the given fields. + /// + public GameParamKey(string Param) + { + this.Param = CheckValidPart(Param, nameof(Param)); + } + + /// + /// The param name, such as SpEffectParam. + /// + public string Param { get; } + + private protected override string InternalName => $"param/GameParam/{Param}.param"; + + /// + public override string ToString() => $"GameParamKey[Param={Param}]"; + /// + public override bool Equals(object obj) => obj is GameParamKey o && Param == o.Param; + /// + public override int GetHashCode() => Param.GetHashCode(); + } + + /// + /// Key for an FMG file in a game, such as English NpcName. + /// + public sealed class FmgKey : FileKey + { + /// + /// Key type which can be used to request and return this type in entity search RPCs. + /// + public static SoulsKeyType KeyType { get; } = + SoulsKeyType.ForFile("msg/{0}/{1}.fmg", typeof(FmgKey)); + + /// + /// Creates a key with the given fields. + /// + public FmgKey(SoulsFmg.FmgLanguage Language, SoulsFmg.FmgType Type) + { + this.Language = Language; + this.Type = Type; + } + + /// + /// The language of this FMG file. + /// + /// Use SoulsFmg to convert this to a per-game folder name. + /// + public SoulsFmg.FmgLanguage Language { get; } + + /// + /// The unique type of this FMG file within the game. + /// + /// Use SoulsFmg to convet this to per-game data representing file locations. + /// + public SoulsFmg.FmgType Type { get; } + + private protected override string InternalName => $"msg/{Language}/{Type}.fmg"; + + /// + public override string ToString() => $"FmgKey[Language={Language},Type={Type}]"; + /// + public override bool Equals(object obj) => obj is FmgKey o && Language == o.Language && Type == o.Type; + /// + public override int GetHashCode() => Language.GetHashCode() ^ Type.GetHashCode(); + } + + /// + /// Key for an MSB file, such as m10_00_00_00 + /// + public sealed class MsbKey : FileKey + { + /// + /// Key type which can be used to request and return this type in entity search RPCs. + /// + public static SoulsKeyType KeyType { get; } = + SoulsKeyType.ForFile("map/mapstudio/{0}.msb", typeof(MsbKey)); + + /// + /// Creates a key with the given fields. + /// + public MsbKey(string Map) + { + this.Map = CheckValidPart(Map, nameof(Map)); + } + + /// + /// The map name, such as m10_00_00_00. + /// + public string Map { get; } + + private protected override string InternalName => $"map/mapstudio/{Map}.msb"; + + /// + public override string ToString() => $"MsbKey[Map={Map}]"; + /// + public override bool Equals(object obj) => obj is MsbKey o && Map == o.Map; + /// + public override int GetHashCode() => Map.GetHashCode(); + } + + /// + /// Key of an unknown format in this library version. + /// + /// Other key types can be added in the future, as well as debug information about unknown + /// keys, but for now this requires both client and server to know about the same keys. + /// + public sealed class UnknownKey : FileKey + { + internal UnknownKey() { } + + private protected override string InternalName => throw new InvalidOperationException(); + + /// + public override string ToString() => "UnknownKey"; + } + + /// + /// Overall key type representing a uniquely identifiable entity within a file. + /// + public abstract class ObjectKey : SoulsKey + { + /// + /// The file key for this object. + /// + public abstract FileKey File { get; } + + /// + /// The namespace for the object's id, if the id is not completely unique. + /// + public KeyNamespace Namespace { get; protected set; } + + /// + /// An index which may be used to disambiguate object ids, if multiple with the same id are allowed. + /// + public int Index { get; protected set; } + + internal abstract object InternalID { get; } + } + + /// + /// Key for a row in a game param, like row 7000 in SpEffectParam. + /// + public sealed class GameParamRowKey : ObjectKey + { + /// + /// Key type which can be used to request and return this type in entity search RPCs. + /// + public static SoulsKeyType KeyType { get; } = + SoulsKeyType.ForEntry(GameParamKey.KeyType, typeof(GameParamRowKey)); + + private readonly GameParamKey file; + + /// + /// Creates a key with the given fields. + /// + public GameParamRowKey(GameParamKey file, int ID, int Index = 0) + { + this.file = file; + this.ID = ID; + this.Index = Index; + } + + /// + /// The param file. + /// + public override GameParamKey File => file; + + /// + /// The row ID. + /// + public int ID { get; } + + internal override object InternalID => ID; + + /// + public override string ToString() => $"GameParamRowKey[Param={File.Param},ID={ID}]"; + /// + public override bool Equals(object obj) => obj is GameParamRowKey o && File.Equals(o.File) && ID == o.ID && Index == o.Index; + /// + public override int GetHashCode() => File.GetHashCode() ^ ID.GetHashCode() ^ Index.GetHashCode(); + } + + /// + /// Key for an entry in an FMG file, like entry 9000 in Japanese PlaceName. + /// + public sealed class FmgEntryKey : ObjectKey + { + /// + /// Key type which can be used to request and return this type in entity search RPCs. + /// + public static SoulsKeyType KeyType { get; } = + SoulsKeyType.ForEntry(FmgKey.KeyType, typeof(FmgEntryKey)); + + private readonly FmgKey file; + + /// + /// Creates a key with the given fields. + /// + public FmgEntryKey(FmgKey file, int ID, int Index = 0) + { + this.file = file; + this.ID = ID; + this.Index = Index; + } + + /// + /// The FMG file. + /// + public override FmgKey File => file; + + /// + /// The entry ID. + /// + public int ID { get; } + + internal override object InternalID => ID; + + /// + public override string ToString() => $"FmgEntryKey[Language={File.Language},Type={File.Type},ID={ID}]"; + /// + public override bool Equals(object obj) => obj is FmgEntryKey o && File.Equals(o.File) && Index == o.Index; + /// + public override int GetHashCode() => File.GetHashCode() ^ Index.GetHashCode(); + } + + /// + /// Key for a named entity in an MSB. + /// + /// This uses the SoulsFormats convention of disambiguating names within a namespace, + /// so that named references can be unambiguous. + /// + public sealed class MsbEntryKey : ObjectKey + { + /// + /// Key type which can be used to request and return this type in entity search RPCs. + /// + public static SoulsKeyType KeyType { get; } = + SoulsKeyType.ForEntry(MsbKey.KeyType, typeof(MsbEntryKey)); + + /// + /// All supported namespaces for MSB entry names. + /// + public static readonly IReadOnlyList Namespaces = new List + { + KeyNamespace.MapEvent, + KeyNamespace.MapRegion, + KeyNamespace.MapPart, + }.AsReadOnly(); + + private readonly MsbKey file; + + /// + /// Creates a key with the given fields. + /// + /// The entity namespace must belong to the Namespaces list. + /// + public MsbEntryKey(MsbKey file, KeyNamespace Namespace, string Name) + { + if (!Namespaces.Contains(Namespace)) + { + throw new ArgumentException($"Invalid map entity namespace {Namespace} (must be one of {string.Join(", ", Namespaces)})"); + } + this.file = file; + this.Namespace = Namespace; + this.Name = Name; + } + + /// + /// The MSB file this entity appears in. + /// + public override MsbKey File => file; + + /// + /// The unique name (within the namespace) of this entity. + /// + public string Name { get; } + + internal override object InternalID => Name; + + /// + public override string ToString() => $"MsbEntryKey[Map={File.Map},Namespace={Namespace},Name={Name}]"; + /// + public override bool Equals(object obj) => + obj is MsbEntryKey o && File.Equals(o.File) && Namespace == o.Namespace && Name == o.Name; + /// + public override int GetHashCode() => File.GetHashCode() ^ Namespace.GetHashCode() ^ Name.GetHashCode(); + } + + internal static readonly HashSet KeyTypes = new HashSet + { + GameParamKey.KeyType, + FmgKey.KeyType, + MsbKey.KeyType, + GameParamRowKey.KeyType, + FmgEntryKey.KeyType, + MsbEntryKey.KeyType, + }; + + private static bool ExtractExtension(string file, string ext, out string name) + { + if (file.EndsWith(ext)) + { + name = file.Substring(0, file.Length - ext.Length); + return true; + } + name = null; + return false; + } + + internal static FileKey ParseFileKey(string str) + { + string[] parts = str.Split('/'); + if (parts.Length == 3 && parts[0] == "param" && parts[1] == "GameParam" && ExtractExtension(parts[2], ".param", out string param)) + { + return new GameParamKey(param); + } + else if (parts.Length == 3 && parts[0] == "msg" && ExtractExtension(parts[2], ".fmg", out string fmg) + && Enum.TryParse(parts[1], out SoulsFmg.FmgLanguage lang) && Enum.TryParse(fmg, out SoulsFmg.FmgType type)) + { + return new FmgKey(lang, type); + } + else if (parts.Length == 3 && parts[0] == "map" && parts[1] == "mapstudio" && ExtractExtension(parts[2], ".msb", out string msb)) + { + return new MsbKey(msb); + } + // At the moment, ignore unknown keys. + // If needed, we can do partial parsing of the filename, for compatibility in future versions, + // but we'd strongly prefer if clients upgrade to use new key types. + return new UnknownKey(); + } + + // Standard invalid characters, and path separators. + // Also disallow whitespace for the time being (can revisit this later). + // https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names + private static readonly Regex invalidFileCharacters = new Regex(@"[\p{C}<>:\\/|?*""\s]", RegexOptions.Compiled); + private static string CheckValidPart(string part, string name) + { + if (part == null) + { + throw new ArgumentNullException(name); + } + if (invalidFileCharacters.IsMatch(part)) + { + throw new ArgumentException($"{name} \"{part}\" contains invalid characters for a file path"); + } + return part; + } + } +} diff --git a/SoapstoneLib/SoapstoneLib/SoulsKeyType.cs b/SoapstoneLib/SoapstoneLib/SoulsKeyType.cs new file mode 100644 index 0000000..dec3264 --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/SoulsKeyType.cs @@ -0,0 +1,42 @@ +using System; +using SoapstoneLib.Proto.Internal; + +namespace SoapstoneLib +{ + /// + /// Representation for a SoulsKey type which can be sent over RPC, including to or from servers not written in C#. + /// + public class SoulsKeyType + { + internal string File { get; } + internal PrimaryKeyCategory Category { get; set; } + internal Type Type { get; set; } + + internal static SoulsKeyType ForFile(string File, Type Type) => + new SoulsKeyType(File, PrimaryKeyCategory.File, Type); + internal static SoulsKeyType ForEntry(SoulsKeyType FileType, Type Type) => + new SoulsKeyType(FileType.File, PrimaryKeyCategory.Entry, Type); + + internal SoulsKeyType(string File, PrimaryKeyCategory Category, Type Type) + { + this.File = File; + this.Category = Category; + this.Type = Type; + } + + /// + /// Determines if a given C# type is compatible with this requested result type. + /// + /// In V1, only class types are used to determine result compatibility. This may change. + /// + public bool Matches(Type resultType) + { + return Type.IsAssignableFrom(resultType); + } + + /// + public override bool Equals(object obj) => obj is SoulsKeyType o && o.File == File && o.Category == Category; + /// + public override int GetHashCode() => File.GetHashCode() ^ Category.GetHashCode(); + } +} diff --git a/SoapstoneLib/SoapstoneLib/SoulsObject.cs b/SoapstoneLib/SoapstoneLib/SoulsObject.cs new file mode 100644 index 0000000..093f2e1 --- /dev/null +++ b/SoapstoneLib/SoapstoneLib/SoulsObject.cs @@ -0,0 +1,356 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using SoapstoneLib.Proto; + +namespace SoapstoneLib +{ + /// + /// Represents an entity in FromSoft game data with a unique key and various properties. + /// + /// Properties are represented as a series of key-value pairs. Multiple values for a + /// given property will result in multiple pairs with that key. + /// + public sealed partial class SoulsObject + { + /// + /// All supported scalar property types. IEnumerable of any of these is also supported via repeated properties. + /// + public static readonly IReadOnlyCollection ScalarPropertyTypes = ImmutableHashSet.Create( + typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(bool), typeof(string)); + + internal IList properties; + + /// + /// Creates a new SoulsObject with the given key and no initial properties. + /// + /// In a request context, properties can be most easily added with AddRequestedProperties. + /// + public SoulsObject(SoulsKey Key) + { + this.Key = Key; + properties = new List(); + } + + internal SoulsObject(SoulsKey Key, IList properties) + { + this.Key = Key; + this.properties = properties; + } + + /// + /// The key identifying this object. + /// + public SoulsKey Key { get; } + + /// + /// Produces the first value of any property with the given key. + /// + public bool TryGetValue(string key, out object value) + { + foreach (Proto.Internal.GameProperty prop in properties) + { + if (prop.Key == key) + { + value = InternalConversions.FromPropertyValue(prop.Value); + return true; + } + } + value = null; + return false; + } + + /// + /// Produces the first value of any property with the given key and output type. + /// + /// This has to match the type exactly. Use TryGetInt if the value is int-convertible. + /// + public bool TryGetValue(string key, out T value) + { + foreach (Proto.Internal.GameProperty prop in properties) + { + if (prop.Key == key) + { + object val = InternalConversions.FromPropertyValue(prop.Value); + if (val is T tval) + { + value = tval; + return true; + } + } + } + value = default; + return false; + } + + /// + /// Produces the first value of any property with the given key, if the value can be converted to an int. + /// + public bool TryGetInt(string key, out int value) + { + value = default; + if (!TryGetValue(key, out object obj)) + { + return false; + } + if (obj is int ival) + { + value = ival; + return true; + } + // We could also special-case uint for performance reasons, but would also have to check for out-of-range values. + // Everything else should be convertible, if not out of range. string does parse here. + try + { + value = Convert.ToInt32(obj); + return true; + } + catch + { + // This is very expensive + return false; + } + } + + /// + /// Produces all values for properties with the given key. + /// + public bool TryGetValues(string key, out List values) + { + values = null; + foreach (Proto.Internal.GameProperty prop in properties) + { + if (prop.Key == key) + { + object value = InternalConversions.FromPropertyValue(prop.Value); + if (values == null) + { + values = new List { value }; + } + else + { + values.Add(value); + } + } + } + return values != null; + } + + /// + /// Produces all values for properties with the given key and output type. + /// + /// The type must match exactly. + /// + public bool TryGetValues(string key, out List values) + { + values = null; + foreach (Proto.Internal.GameProperty prop in properties) + { + if (prop.Key == key) + { + object value = InternalConversions.FromPropertyValue(prop.Value); + if (value is T tval) + { + if (values == null) + { + values = new List { tval }; + } + else + { + values.Add(tval); + } + } + } + } + return values != null; + } + + /// + /// Produces all values for properties with the given key, if the values can be converted to an int. + /// + public bool TryGetInts(string key, out List values) + { + values = null; + foreach (Proto.Internal.GameProperty prop in properties) + { + if (prop.Key == key) + { + object obj = InternalConversions.FromPropertyValue(prop.Value); + // Adapted from TryGetInt + if (obj is not int value) + { + try + { + value = Convert.ToInt32(obj); + } + catch + { + continue; + } + } + if (values == null) + { + values = new List { value }; + } + else + { + values.Add(value); + } + } + } + return values != null; + } + + /// + /// Ordered enumerable of all key-value property pairs. + /// + public IEnumerable Properties => properties.Select(InternalConversions.FromGameProperty); + + /// + /// Ordered enumerable of all distinct property key names. + /// + public IEnumerable PropertyKeys => properties.Select(p => p.Key).Distinct(); + + /// + /// A key-value dictionary for properties. + /// + /// If a key has multiple values, only the first one is returned. + /// + public Dictionary FirstPropertyValues + { + get + { + Dictionary dict = new Dictionary(); + foreach (Proto.Internal.GameProperty prop in properties) + { + if (!dict.ContainsKey(prop.Key)) + { + dict[prop.Key] = InternalConversions.FromPropertyValue(prop.Value); + } + } + return dict; + } + } + + /// + /// Adds the given key-value property. The value's type should be in ScalarPropertyTypes. + /// + public void AddProperty(string key, object value) + { + properties.Add(new Proto.Internal.GameProperty { Key = key, Value = InternalConversions.ToPropertyValue(value) }); + } + + /// + /// Adds the given Property instance. The property value's type should be in ScalarPropertyTypes. + /// + /// + public void AddProperty(Property prop) + { + properties.Add(new Proto.Internal.GameProperty { Key = prop.Key, Value = InternalConversions.ToPropertyValue(prop.Value) }); + } + + /// + /// Adds the key property with associated values. The values' types should be in ScalarPropertyTypes. + /// + public void AddProperties(string key, IEnumerable values) + { + foreach (object value in values) + { + properties.Add(new Proto.Internal.GameProperty { Key = key, Value = InternalConversions.ToPropertyValue(value) }); + } + } + + /// + /// Adds the given Property instances. The property value types should be in ScalarPropertyTypes. + /// + /// + public void AddProperties(IEnumerable props) + { + foreach (Property prop in props) + { + properties.Add(new Proto.Internal.GameProperty { Key = prop.Key, Value = InternalConversions.ToPropertyValue(prop.Value) }); + } + } + + /// + /// Adds all properties corresponding to RequestedProperties given by a client. + /// + /// The accessor function takes a property key and returns a value. If it returns null or + /// an unsupported scalar type (not present in ScalarPropertyTypes), the property is not added. + /// If it returns an IEnumerable of values, each value is added individually. + /// + public void AddRequestedProperties(RequestedProperties props, Func accessor) + { + foreach (RequestedProperty requested in props.Properties) + { + string key = requested.Key; + object res = accessor.Invoke(key); + if (res == null) + { + continue; + } + if (res is not string && res is System.Collections.IEnumerable multi) + { + foreach (object val in multi) + { + if (val == null) + { + continue; + } + if (ScalarPropertyTypes.Contains(val.GetType()) + && (!requested.NonTrivialOnly || RequestedProperties.IsNonTrivialValue(val))) + { + AddProperty(key, val); + } + } + } + else if (ScalarPropertyTypes.Contains(res.GetType()) + && (!requested.NonTrivialOnly || RequestedProperties.IsNonTrivialValue(res))) + { + AddProperty(key, res); + } + } + } + + /// + /// Reset all properties. + /// + public void ClearProperties() + { + properties.Clear(); + } + + /// + public override string ToString() => $"{Key}[{string.Join(",", Properties)}]"; + + /// + /// Represents a key-value pair of a game object. + /// + public sealed class Property + { + /// + /// Creates a Property with the given fields. + /// + public Property(string Key, object Value) + { + this.Key = Key; + this.Value = Value; + } + + /// + /// Lookup key for the property. + /// + public string Key { get; } + + /// + /// Value for the property, which must be one of ScalarPropertyTypes. + /// + public object Value { get; } + + /// + public override string ToString() => $"{Key}={Value}"; + } + } +} diff --git a/SoapstoneLib/TestConsoleApp/FmgData.cs b/SoapstoneLib/TestConsoleApp/FmgData.cs new file mode 100644 index 0000000..aacf8a2 --- /dev/null +++ b/SoapstoneLib/TestConsoleApp/FmgData.cs @@ -0,0 +1,724 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using SoulsFormats; +using SoapstoneLib.Proto; + +namespace TestConsoleApp +{ + internal class FmgData + { + private static readonly Dictionary gamePathParts = new Dictionary + { + [FromSoftGame.DemonsSouls] = "Demons Souls", + [FromSoftGame.DarkSoulsPtde] = "Dark Souls Prepare to Die Edition", + [FromSoftGame.DarkSoulsRemastered] = "DARK SOULS REMASTERED", + [FromSoftGame.DarkSouls2] = "Dark Souls II", + [FromSoftGame.DarkSouls2Sotfs] = "Dark Souls II Scholar of the First Sin", + [FromSoftGame.Bloodborne] = "Bloodborne", + [FromSoftGame.DarkSouls3] = "DARK SOULS III", + [FromSoftGame.Sekiro] = "Sekiro", + [FromSoftGame.EldenRing] = "ELDEN RING", + }; + + public class FmgInfo + { + public FromSoftGame Game { get; set; } + public string FmgPath { get; set; } + public string FmgName { get; set; } + public string Category { get; set; } + public string BinderPath { get; set; } + public int BinderID { get; set; } = -1; + + public static string GetName(string path) + { + if (!path.EndsWith(".fmg")) throw new Exception(path); + return Path.GetFileNameWithoutExtension(Path.GetFileName(path)); + } + + public override string ToString() => BinderPath == null ? $"{Game} {FmgPath}" : $"{Game} {BinderPath} {BinderID}:{FmgPath}"; + } + + public static void Run(string[] args) + { + Dictionary gamePaths = new Dictionary(); + foreach (KeyValuePair entry in gamePathParts) + { + string pathPart = entry.Value.ToLowerInvariant(); + string gameArg = args.Where(a => a.ToLowerInvariant().Split('\\').Contains(pathPart)).FirstOrDefault(); + if (gameArg == null) + { + throw new Exception($"Path for {entry.Key} not provided: path part \"{entry.Value}\" not found"); + } + gamePaths[entry.Key] = gameArg; + } + List infos = new List(); + void process(string relPath, string path, FromSoftGame game) + { + // Filter out non-FMGs + if ((!relPath.ToLowerInvariant().StartsWith("msg") && !relPath.ToLowerInvariant().StartsWith(@"menu\text")) + // Only use dlc2 msgbnds + || (game == FromSoftGame.DarkSouls3 && relPath.Contains("dlc1.msgbnd")) + // DCX and non-DCX versions are present in DeS + || (game == FromSoftGame.DemonsSouls && relPath.EndsWith("msgbnd")) + // Japanese is not the main language in this case, jpnjp is + || ((game == FromSoftGame.Bloodborne || game == FromSoftGame.Sekiro) && relPath.Contains(@"\japanese\")) + // Don't care about these at present. The first is DeS, sellregion is BB and later + || relPath.EndsWith("sample.msgbnd.dcx") + || relPath.EndsWith("sellregion.msgbnd.dcx") + || relPath.EndsWith("ngword.msgbnd.dcx")) + { + return; + } + byte[] bndBytes; + if (path.EndsWith("bnd.dcx") && DCX.Is(path)) + { + bndBytes = DCX.Decompress(path); + } + else if (path.EndsWith("bnd")) + { + bndBytes = File.ReadAllBytes(path); + } + else + { + if (path.EndsWith(".fmg")) + { + infos.Add(new FmgInfo + { + Game = game, + FmgPath = relPath, + FmgName = FmgInfo.GetName(relPath), + }); + } + return; + } + IBinder bnd; + if (BND3.IsRead(bndBytes, out BND3 bnd3)) + { + bnd = bnd3; + } + else if (BND4.IsRead(bndBytes, out BND4 bnd4)) + { + bnd = bnd4; + } + else + { + return; + } + foreach (BinderFile bndFile in bnd.Files) + { + string fileName = bndFile.Name; + FmgInfo info = new FmgInfo + { + Game = game, + FmgPath = fileName, + FmgName = FmgInfo.GetName(fileName), + BinderPath = relPath, + BinderID = bndFile.ID, + }; + infos.Add(info); + } + } + void dirSearch(string dir, string basePath, FromSoftGame game) + { + string dirName = Path.GetFileName(dir); + if (dirName == "vanilla" || dirName == "dcx" || dirName.StartsWith("old_patch")) + { + return; + } + foreach (string path in Directory.GetFiles(dir)) + { + if (!path.StartsWith(basePath)) + { + throw new Exception($"Bad prefix {path}"); + } + string subPath = path.Substring(basePath.Length).TrimStart(Path.DirectorySeparatorChar); + if (subPath.EndsWith(".txt") && subPath.StartsWith("msg")) + { + continue; + } + process(subPath, path, game); + } + foreach (string subDir in Directory.GetDirectories(dir)) + { + dirSearch(subDir, basePath, game); + } + } + foreach (KeyValuePair entry in gamePaths) + { + if (args.Contains("key")) + { + Console.WriteLine(entry.Key); + } + string gamePath = Path.GetFullPath(entry.Value); + dirSearch(gamePath, gamePath, entry.Key); + } + List cats = new List { "item", "menu" }; + string capitalize(string s) => s.Substring(0, 1).ToUpperInvariant() + s.Substring(1); + string getCategory(FmgInfo info) + { + if (info.BinderPath == null) + { + return "none"; + } + foreach (string cat in cats) + { + if (info.BinderPath.Contains(@"\" + cat)) + { + return cat; + } + } + throw new Exception($"Unknown category in {info}"); + } + string getLanguage(FmgInfo info) + { + string path = info.BinderPath ?? info.FmgPath; + string lang; + if (path.StartsWith(@"msg\")) + { + lang = path.Split('\\')[1].ToLowerInvariant(); + if (info.Game == FromSoftGame.DemonsSouls && lang.Contains("msgbnd")) + { + lang = "japanese"; + } + } + else if (path.StartsWith(@"menu\text\")) + { + lang = path.Split('\\')[2].ToLowerInvariant(); + } + else throw new Exception($"Unknown language in {info}"); + return lang; + } + List suffixes = new List { "_DLC1", "_DLC2", "_Patch" }; + string getBaseEnumName(string name) + { + foreach (string suffix in suffixes) + { + if (name.EndsWith(suffix)) + { + return name.Substring(0, name.Length - suffix.Length); + } + } + return name; + } + Dictionary<(int, string), string> keyNames = new(); + foreach (string text in fmgEnums) + { + string[] parts = text.Split('/'); + if (parts[2].Length > 0) + { + keyNames[(int.Parse(parts[0]), parts[1])] = parts[2]; + } + } + if (args.Contains("key")) + { + SortedDictionary<(int, string), List> byKey = new(); + foreach (FmgInfo info in infos) + { + if (info.FmgName.EndsWith("_00")) + { + continue; + } + (int, string) key = (info.BinderID, info.FmgName); + AddMulti(byKey, key, info); + Console.WriteLine(info); + } + string showAll(List infos, Func func) => string.Join(", ", infos.Select(func).Distinct()); + foreach (KeyValuePair<(int, string), List> entry in byKey) + { + int id = entry.Key.Item1; + string name = entry.Key.Item2; + keyNames.TryGetValue(entry.Key, out string enumName); + if (enumName == null) + { + foreach (string suffix in suffixes) + { + // The FMG name can be used to infer the enum name (for _dlc1 and _dlc2, which are lowercase suffixes) + if (name.EndsWith(suffix.ToLowerInvariant())) + { + string shortName = name.Substring(0, name.Length - suffix.Length); + List otherNames = keyNames.Where(e => e.Key.Item2 == shortName).Select(e => e.Value).ToList(); + if (otherNames.Count == 1) + { + enumName = otherNames[0] + suffix; + break; + } + } + } + } + string comment = $"{showAll(entry.Value, getCategory)} [{showAll(entry.Value, i => i.Game)}]"; + Console.WriteLine($"\"{id}/{name}/{enumName}\", // {comment}"); + } + Console.WriteLine(); + Dictionary> langInfos = infos.GroupBy(getLanguage).ToDictionary(e => e.Key, e => e.ToList()); + foreach (KeyValuePair> entry in langInfos) + { + string comment = $"[{showAll(entry.Value, i => i.Game)}]"; + languageEnums.TryGetValue(entry.Key, out string name); + Console.WriteLine($"[\"{entry.Key}\"] = \"{name}\", // {comment}"); + } + } + if (args.Contains("data")) + { + List langs = new() { "Unspecified" }; + langs.AddRange(languageEnums.Values.OrderBy(s => s).Distinct()); + List types = new() { "Unspecified" }; + types.AddRange(keyNames.Values.OrderBy(s => s).Distinct()); + string quote(string s) => $"\"{s}\""; + SortedDictionary>> gameData = new(); + foreach (FromSoftGame game in gamePaths.Keys) + { + // This is based on FmgGameInfo. Reflection-based serialization might be a bit cleaner than this? + SortedDictionary byType = new(); + SortedDictionary> overrides = new(); + SortedDictionary> byFmgName = new(); + SortedDictionary> byBinderID = new(); + SortedDictionary toLanguageEnum = new(); + foreach (FmgInfo info in infos) + { + if (info.Game != game || info.FmgName.EndsWith("_00")) + { + continue; + } + (int, string) key = (info.BinderID, info.FmgName); + if (!keyNames.TryGetValue(key, out string type)) + { + throw new Exception($"Unrecognized FMG {info}"); + } + string baseType = getBaseEnumName(type); + string category = capitalize(getCategory(info)); + string nameSrc = quote(info.FmgName); + string newSrc = $"new FmgKeyInfo(FromSoftGame.{game}, FmgCategory.{category}, FmgType.{type}, FmgType.{baseType}, {nameSrc}, {info.BinderID})"; + string typeSrc = $"FmgType.{type}"; + if (byType.TryGetValue(typeSrc, out string existNew)) + { + if (newSrc != existNew) + { + throw new Exception($"Mismatched constructors per type: {newSrc} vs {existNew}"); + } + } + byType[typeSrc] = newSrc; + string refSrc = typeSrc; + if (type != baseType) + { + AddMulti(overrides, $"FmgType.{baseType}", refSrc); + } + AddMulti(byFmgName, nameSrc, refSrc); + if (info.BinderID >= 0) + { + AddMulti(byBinderID, info.BinderID, refSrc); + } + string lang = getLanguage(info); + toLanguageEnum[quote(lang)] = $"FmgLanguage.{languageEnums[lang]}"; + } + if (game == FromSoftGame.DarkSouls2 || game == FromSoftGame.DarkSouls2Sotfs) + { + // Global version of Steam DS2 doesn't have Japanese text, so add it here artificially + // Still, it won't be available for many modders. + toLanguageEnum[quote("japanese")] = "FmgLanguage.Japanese"; + } + string refList(IEnumerable refs) => $"new List {{ {string.Join(", ", refs.Distinct())} }}"; + gameData[game] = new() + { + ["ByType"] = byType, + ["Overrides"] = overrides.ToDictionary(e => e.Key, e => refList(Enumerable.Reverse(e.Value))), + ["ByFmgName"] = byFmgName.ToDictionary(e => e.Key, e => refList(e.Value)), + ["ByBinderID"] = byBinderID.ToDictionary(e => e.Key.ToString(), e => refList(e.Value)), + ["ToLanguageEnum"] = toLanguageEnum, + ["FromLanguageEnum"] = toLanguageEnum.ToDictionary(e => e.Value, e => e.Key), + }; + } + string indent = " "; + string indent2 = " " + indent; + string indent3 = " " + indent2; + Console.Write(@$"using System.Collections.Generic; +using SoapstoneLib.Proto; + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +// This file is autogenerated by TestConsoleApp. +// DO NOT MANUALLY EDIT THIS FILE. + +namespace SoapstoneLib +{{ + public static partial class SoulsFmg + {{ + /// + /// The language categorization for any FMG in any supported game. + /// + /// This corresponds to a language folder in the game files. + /// + /// Do not rely on the integer value of this enum, as it may change over time. Only use the name. + /// + public enum FmgLanguage + {{ +{string.Join("\n", langs.Select(s => $"{indent}{s},"))} + }} + + /// + /// Type specifier for any item or menu FMG in any supported game. + /// + /// Each FMG file within a game has a unique type, within any given language. + /// + /// Do not rely on the integer value of this enum, as it may change over time. Only use the name. + /// + public enum FmgType + {{ +{string.Join("\n", types.Select(s => $"{indent}{s},"))} + }} + + internal static bool TryGetFmgGameInfo(FromSoftGame game, out FmgGameInfo info) + {{ + if (fmgGames == null) + {{ + fmgGames = MakeFmgGames(); + }} + return fmgGames.TryGetValue(game, out info); + }} + + // Lazily initialize this to keep things lightweight if FMG is not needed. + // Not synchronizing initialization should be fine. + private static Dictionary fmgGames; + private static Dictionary MakeFmgGames() => new() + {{ +{string.Join("\n", gameData.Select(e => @$"{indent}[FromSoftGame.{e.Key}] = new FmgGameInfo +{indent}{{ +{string.Join("\n", e.Value.Select(e2 => @$"{indent2}{e2.Key} = new() +{indent2}{{ +{string.Join("\n", e2.Value.Select(e3 => @$"{indent3}[{e3.Key}] = {e3.Value},"))} +{indent2}}},"))} +{indent}}},"))} + }}; + }} +}} +".Replace("\r\n", "\n")); + } + } + + private static void AddMulti(IDictionary dict, K key, V value) where T : ICollection, new() + { + if (!dict.TryGetValue(key, out T col)) + { + dict[key] = col = new T(); + } + col.Add(value); + } + + private static readonly Dictionary languageEnums = new() + { + ["japanese"] = "Japanese", // [DemonsSouls, DarkSoulsPtde, DarkSoulsRemastered, Bloodborne, Sekiro] + ["asia_english"] = "AsiaEnglish", // [DemonsSouls] + ["french"] = "French", // [DemonsSouls, DarkSoulsPtde, DarkSoulsRemastered, DarkSouls2, DarkSouls2Sotfs] + ["german"] = "German", // [DemonsSouls, DarkSoulsPtde, DarkSoulsRemastered] + ["italian"] = "Italian", // [DemonsSouls, DarkSoulsPtde, DarkSoulsRemastered, DarkSouls2, DarkSouls2Sotfs] + ["korean"] = "Korean", // [DemonsSouls, DarkSoulsPtde, DarkSoulsRemastered, DarkSouls2, DarkSouls2Sotfs] + ["na_english"] = "English", // [DemonsSouls] + ["spanish"] = "SpainSpanish", // [DemonsSouls, DarkSoulsPtde, DarkSoulsRemastered, DarkSouls2, DarkSouls2Sotfs] + ["tchinese"] = "TraditionalChinese", // [DemonsSouls, DarkSoulsPtde, DarkSoulsRemastered] + ["uk_english"] = "BritishEnglish", // [DemonsSouls] + ["english"] = "English", // [DarkSoulsPtde, DarkSoulsRemastered, DarkSouls2, DarkSouls2Sotfs] + ["polish"] = "Polish", // [DarkSoulsPtde, DarkSoulsRemastered, DarkSouls2, DarkSouls2Sotfs] + ["russian"] = "Russian", // [DarkSoulsPtde, DarkSoulsRemastered, DarkSouls2, DarkSouls2Sotfs] + ["nspanish"] = "Spanish", // [DarkSoulsRemastered] + ["portuguese"] = "BrazilPortuguese", // [DarkSoulsRemastered, DarkSouls2, DarkSouls2Sotfs] + ["schinese"] = "SimplifiedChinese", // [DarkSoulsRemastered] + ["chinese"] = "TraditionalChinese", // [DarkSouls2, DarkSouls2Sotfs] + ["germany"] = "German", // [DarkSouls2, DarkSouls2Sotfs] + ["neutralspanish"] = "Spanish", // [DarkSouls2Sotfs] + ["dandk"] = "Danish", // [Bloodborne] + ["deude"] = "German", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["enggb"] = "BritishEnglish", // [Bloodborne] + ["engus"] = "English", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["finfi"] = "Finnish", // [Bloodborne] + ["frafr"] = "French", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["itait"] = "Italian", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["jpnjp"] = "Japanese", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["korkr"] = "Korean", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["nldnl"] = "Dutch", // [Bloodborne] + ["norno"] = "Norwegian", // [Bloodborne] + ["polpl"] = "Polish", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["porbr"] = "BrazilPortuguese", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["porpt"] = "PortugalPortuguese", // [Bloodborne] + ["rusru"] = "Russian", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["spaar"] = "Spanish", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["spaes"] = "SpainSpanish", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["swese"] = "Swedish", // [Bloodborne] + ["turtr"] = "Turkish", // [Bloodborne] + ["zhocn"] = "SimplifiedChinese", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["zhotw"] = "TraditionalChinese", // [Bloodborne, DarkSouls3, Sekiro, EldenRing] + ["thath"] = "Thai", // [Sekiro, EldenRing] + }; + + private static readonly List fmgEnums = new() + { + "-1/bloodmessageconjunction/BloodMsgConjunction", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/bloodmessagesentence/BloodMsgSentence", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/bloodmessageword/BloodMsgWord", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/bloodmessagewordcategory/BloodMsgWordCategory", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/bofire/BonfireMenu", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/bonfirename/BonfireName", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/charamaking/CharaMaking", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/charaname/CharaName", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/common/DS2Common", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/dconlymessage/SystemMessageDC", // none [DarkSouls2Sotfs] + "-1/detailedexplanation/ItemCaption", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/iconhelp/IconHelp", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/ingamemenu/InGameMenu", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/ingamesystem/InGameSystem", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/itemname/ItemName", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/keyguide/KeyGuide", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/mapevent/EventTextForMap", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/mapname/PlaceName", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/npcmenu/NpcMenu", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/pluralselect/PluralSelect", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/prologue/Prologue", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/shop/DS2Shop", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/simpleexplanation/ItemInfo", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/staffroll/StaffRoll", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/titleflow/TitleFlow", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/titlemenu/TitleMenu", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/weapontype/WeaponType", // none [DarkSouls2, DarkSouls2Sotfs] + "-1/win32onlymessage/SystemMessageWindows", // none [DarkSouls2, DarkSouls2Sotfs] + "1/Conversation_/TalkMsg", // menu [DarkSoulsRemastered] + "1/TalkMsg/TalkMsg", // menu [EldenRing] + "1/会話/TalkMsg", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "2/Blood_writing_/BloodMsg", // menu [DarkSoulsRemastered] + "2/BloodMsg/BloodMsg", // menu [EldenRing] + "2/血文字/BloodMsg", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "3/Movie_subtitles_/MovieSubtitle", // menu [DarkSoulsRemastered] + "3/MovieSubtitle/MovieSubtitle", // menu [EldenRing] + "3/ムービー字幕/MovieSubtitle", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "4/TalkMsg_FemalePC_Alt/TalkMsgFemalePCAlt", // menu [EldenRing] + "4/死因/CauseOfDeath", // menu [Bloodborne] + "10/GoodsName/GoodsName", // item [EldenRing] + "10/Item_name_/GoodsName", // item [DarkSoulsRemastered] + "10/アイテム名/GoodsName", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "11/Weapon_name_/WeaponName", // item [DarkSoulsRemastered] + "11/WeaponName/WeaponName", // item [EldenRing] + "11/武器名/WeaponName", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "12/Armor_name_/ProtectorName", // item [DarkSoulsRemastered] + "12/ProtectorName/ProtectorName", // item [EldenRing] + "12/防具名/ProtectorName", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "13/Accessory_name_/AccessoryName", // item [DarkSoulsRemastered] + "13/AccessoryName/AccessoryName", // item [EldenRing] + "13/アクセサリ名/AccessoryName", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "14/Magic_name_/MagicName", // item [DarkSoulsRemastered] + "14/MagicName/MagicName", // item [EldenRing] + "14/魔法名/MagicName", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "15/Feature_name_/FeatureName", // item [DarkSoulsRemastered] + "15/特徴名/FeatureName", // item [DemonsSouls, DarkSoulsPtde, Bloodborne] + "16/Feature_description_/FeatureInfo", // item [DarkSoulsRemastered] + "16/特徴説明/FeatureInfo", // item [DemonsSouls, DarkSoulsPtde, Bloodborne] + "17/Feature_long_desc_/FeatureCaption", // item [DarkSoulsRemastered] + "17/特徴うんちく/FeatureCaption", // item [DemonsSouls, DarkSoulsPtde, Bloodborne] + "18/NPC_name_/NpcName", // item [DarkSoulsRemastered] + "18/NpcName/NpcName", // item [EldenRing] + "18/NPC名/NpcName", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "19/Place_name_/PlaceName", // item [DarkSoulsRemastered] + "19/PlaceName/PlaceName", // item [EldenRing] + "19/地名/PlaceName", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "20/GoodsInfo/GoodsInfo", // item [EldenRing] + "20/Item_description_/GoodsInfo", // item [DarkSoulsRemastered] + "20/アイテム説明/GoodsInfo", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "21/Weapon_description_/WeaponInfo", // item [DarkSoulsRemastered] + "21/WeaponInfo/WeaponInfo", // item [EldenRing] + "21/武器説明/WeaponInfo", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "22/Armor_description_/ProtectorInfo", // item [DarkSoulsRemastered] + "22/ProtectorInfo/ProtectorInfo", // item [EldenRing] + "22/防具説明/ProtectorInfo", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "23/Accessory_description_/AccessoryInfo", // item [DarkSoulsRemastered] + "23/AccessoryInfo/AccessoryInfo", // item [EldenRing] + "23/アクセサリ説明/AccessoryInfo", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "24/GoodsCaption/GoodsCaption", // item [EldenRing] + "24/Item_long_desc_/GoodsCaption", // item [DarkSoulsRemastered] + "24/アイテムうんちく/GoodsCaption", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "25/Weapon_long_desc_/WeaponCaption", // item [DarkSoulsRemastered] + "25/WeaponCaption/WeaponCaption", // item [EldenRing] + "25/武器うんちく/WeaponCaption", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "26/Armor_long_desc_/ProtectorCaption", // item [DarkSoulsRemastered] + "26/ProtectorCaption/ProtectorCaption", // item [EldenRing] + "26/防具うんちく/ProtectorCaption", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "27/Accessory_long_desc_/AccessoryCaption", // item [DarkSoulsRemastered] + "27/AccessoryCaption/AccessoryCaption", // item [EldenRing] + "27/アクセサリうんちく/AccessoryCaption", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "28/Magic_description_/MagicInfo", // item [DarkSoulsRemastered] + "28/MagicInfo/MagicInfo", // item [EldenRing] + "28/魔法説明/MagicInfo", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "29/Magic_long_desc_/MagicCaption", // item [DarkSoulsRemastered] + "29/MagicCaption/MagicCaption", // item [EldenRing] + "29/魔法うんちく/MagicCaption", // item [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "30/Event_text_/EventText", // menu [DarkSoulsRemastered] + "30/イベントテキスト/EventText", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "31/NetworkMessage/NetworkMessage", // menu [EldenRing] + "31/魔石名/GemName", // menu [Bloodborne] + "32/ActionButtonText/ActionButtonText", // menu [EldenRing] + "32/魔石説明/GemInfo", // menu [Bloodborne] + "33/EventTextForTalk/EventTextForTalk", // menu [EldenRing] + "33/魔石うんちく/GemCaption", // menu [Bloodborne] + "34/EventTextForMap/EventTextForMap", // menu [EldenRing] + "34/魔石接頭語/GemPrefix", // menu [Bloodborne] + "35/GemName/GemName", // item [EldenRing] + "35/魔石効果/GemEffect", // menu [Bloodborne] + "36/GemInfo/GemInfo", // item [EldenRing] + "37/GemCaption/GemCaption", // item [EldenRing] + "40/戦技種別/Skills", // item [DarkSouls3, Sekiro] + "41/GoodsDialog/GoodsDialog", // item [EldenRing] + "42/ArtsName/ArtsName", // item [EldenRing] + "43/ArtsCaption/ArtsCaption", // item [EldenRing] + "44/WeaponEffect/WeaponEffect", // item [EldenRing] + "45/GemEffect/GemEffect", // item [EldenRing] + "46/GoodsInfo2/GoodsInfo2", // item [EldenRing] + "70/Ingame_menu_/InGameMenu", // menu [DarkSoulsRemastered] + "70/インゲームメニュー/InGameMenu", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "76/Menu_general_text_/MenuGeneralText", // menu [DarkSoulsRemastered] + "76/メニュー共通テキスト/MenuGeneralText", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "77/Menu_others_/MenuOther", // menu [DarkSoulsRemastered] + "77/メニューその他/MenuOther", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "78/Dialogue_/Dialogues", // menu [DarkSoulsRemastered] + "78/ダイアログ/Dialogues", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "79/Key_guide_/KeyGuide", // menu [DarkSoulsRemastered] + "79/キーガイド/KeyGuide", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "80/Single_line_help_/LineHelp", // menu [DarkSoulsRemastered] + "80/一行ヘルプ/LineHelp", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "81/Item_help_/MenuContext", // menu [DarkSoulsRemastered] + "81/項目ヘルプ/MenuContext", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "90/Text_display_tag_list_/TextDisplayTagList", // menu [DarkSoulsRemastered] + "90/テキスト表示用タグ一覧/TextDisplayTagList", // menu [DemonsSouls, DarkSoulsPtde, Bloodborne, DarkSouls3, Sekiro] + "91/System_specific_tags_win32_/SystemTagsWindows", // menu [DarkSoulsRemastered] + "91/機種別タグ_win32/SystemTagsWindows", // menu [DarkSoulsPtde] + "91/機種別タグ_win64/SystemTagsWindows", // menu [Bloodborne] + "92/System_message_win32_/SystemMessageWindows", // menu [DarkSoulsRemastered] + "92/システムメッセージ_win32/SystemMessageWindows", // menu [DarkSoulsPtde] + "92/システムメッセージ_win64/SystemMessageWindows", // menu [Bloodborne, Sekiro] + "100/Item_long_desc_/GoodsCaption_Patch", // item [DarkSoulsRemastered] + "100/アイテムうんちくパッチ/GoodsCaption_Patch", // menu [DarkSoulsPtde] + "101/Event_text_/EventText_Patch", // menu [DarkSoulsRemastered] + "101/イベントテキストパッチ/EventText_Patch", // menu [DarkSoulsPtde] + "102/Dialogue_/Dialogues_Patch", // menu [DarkSoulsRemastered] + "102/ダイアログパッチ/Dialogues_Patch", // menu [DarkSoulsPtde] + "103/System_message_win32_/SystemMessageWindows_Patch", // menu [DarkSoulsRemastered] + "103/システムメッセージ_win32パッチ/SystemMessageWindows_Patch", // menu [DarkSoulsPtde] + "104/Conversation_/TalkMsg_Patch", // menu [DarkSoulsRemastered] + "104/会話パッチ/TalkMsg_Patch", // menu [DarkSoulsPtde] + "105/Magic_long_desc_/MagicCaption_Patch", // item [DarkSoulsRemastered] + "105/魔法うんちくパッチ/MagicCaption_Patch", // menu [DarkSoulsPtde] + "106/Weapon_long_desc_/WeaponCaption_Patch", // item [DarkSoulsRemastered] + "106/武器うんちくパッチ/WeaponCaption_Patch", // menu [DarkSoulsPtde] + "107/Blood_writing_/BloodMsg_Patch", // menu [DarkSoulsRemastered] + "107/血文字パッチ/BloodMsg_Patch", // menu [DarkSoulsPtde] + "108/Armor_long_desc_/ProtectorCaption_Patch", // item [DarkSoulsRemastered] + "108/防具うんちくパッチ/ProtectorCaption_Patch", // menu [DarkSoulsPtde] + "109/Accessory_long_desc_/AccessoryCaption_Patch", // item [DarkSoulsRemastered] + "109/アクセサリうんちくパッチ/AccessoryCaption_Patch", // menu [DarkSoulsPtde] + "110/Item_description_/GoodsInfo_Patch", // item [DarkSoulsRemastered] + "110/アイテム説明パッチ/GoodsInfo_Patch", // menu [DarkSoulsPtde] + "111/Item_name_/GoodsName_Patch", // item [DarkSoulsRemastered] + "111/アイテム名パッチ/GoodsName_Patch", // menu [DarkSoulsPtde] + "112/Accessory_description_/AccessoryInfo_Patch", // item [DarkSoulsRemastered] + "112/アクセサリ説明パッチ/AccessoryInfo_Patch", // menu [DarkSoulsPtde] + "113/Accessory_name_/AccessoryName_Patch", // item [DarkSoulsRemastered] + "113/アクセサリ名パッチ/AccessoryName_Patch", // menu [DarkSoulsPtde] + "114/Weapon_description_/WeaponInfo_Patch", // item [DarkSoulsRemastered] + "114/武器説明パッチ/WeaponInfo_Patch", // menu [DarkSoulsPtde] + "115/Weapon_name_/WeaponName_Patch", // item [DarkSoulsRemastered] + "115/武器名パッチ/WeaponName_Patch", // menu [DarkSoulsPtde] + "116/Armor_description_/ProtectorInfo_Patch", // item [DarkSoulsRemastered] + "116/防具説明パッチ/ProtectorInfo_Patch", // menu [DarkSoulsPtde] + "117/Armor_name_/ProtectorName_Patch", // item [DarkSoulsRemastered] + "117/防具名パッチ/ProtectorName_Patch", // menu [DarkSoulsPtde] + "118/Magic_name_/MagicName_Patch", // item [DarkSoulsRemastered] + "118/魔法名パッチ/MagicName_Patch", // menu [DarkSoulsPtde] + "119/NPC_name_/NpcName_Patch", // item [DarkSoulsRemastered] + "119/NPC名パッチ/NpcName_Patch", // menu [DarkSoulsPtde] + "120/Place_name_/PlaceName_Patch", // item [DarkSoulsRemastered] + "120/地名パッチ/PlaceName_Patch", // menu [DarkSoulsPtde] + "121/Single_line_help_/LineHelp_Patch", // menu [DarkSoulsRemastered] + "121/一行ヘルプパッチ/LineHelp_Patch", // menu [DarkSoulsPtde] + "122/Key_guide_/KeyGuide_Patch", // menu [DarkSoulsRemastered] + "122/キーガイドパッチ/KeyGuide_Patch", // menu [DarkSoulsPtde] + "123/Menu_others_/MenuOther_Patch", // menu [DarkSoulsRemastered] + "123/メニューその他パッチ/MenuOther_Patch", // menu [DarkSoulsPtde] + "124/Menu_general_text_/MenuGeneralText_Patch", // menu [DarkSoulsRemastered] + "124/メニュー共通テキストパッチ/MenuGeneralText_Patch", // menu [DarkSoulsPtde] + "200/FDP_メニューテキスト/GameMenuText", // menu [DarkSouls3, Sekiro] + "200/GR_MenuText/GameMenuText", // menu [EldenRing] + "200/NTC_メニューテキスト/GameMenuText", // menu [Sekiro] + "200/SP_メニューテキスト/GameMenuText", // menu [Bloodborne] + "201/FDP_一行ヘルプ/GameLineHelp", // menu [DarkSouls3, Sekiro] + "201/GR_LineHelp/GameLineHelp", // menu [EldenRing] + "201/NTC_一行ヘルプ/GameLineHelp", // menu [Sekiro] + "201/SP_一行ヘルプ/GameLineHelp", // menu [Bloodborne] + "202/FDP_キーガイド/GameKeyGuide", // menu [DarkSouls3, Sekiro] + "202/GR_KeyGuide/GameKeyGuide", // menu [EldenRing] + "202/NTC_キーガイド/GameKeyGuide", // menu [Sekiro] + "202/SP_キーガイド/GameKeyGuide", // menu [Bloodborne] + "203/FDP_システムメッセージ_win64/GameSystemMessageWindows", // menu [DarkSouls3, Sekiro] + "203/GR_System_Message_win64/GameSystemMessageWindows", // menu [EldenRing] + "203/NTC_システムメッセージ_win64/GameSystemMessageWindows", // menu [Sekiro] + "203/SP_システムメッセージ_win64/GameSystemMessageWindows", // menu [Bloodborne] + "204/FDP_ダイアログ/GameDialogues", // menu [DarkSouls3, Sekiro] + "204/GR_Dialogues/GameDialogues", // menu [EldenRing] + "204/NTC_ダイアログ/GameDialogues", // menu [Sekiro] + "204/SP_ダイアログ/GameDialogues", // menu [Bloodborne] + "205/FDP_システムメッセージ_ps4/GameSystemMessagePS4", // menu [DarkSouls3] + "205/LoadingTitle/LoadingTitle", // menu [EldenRing] + "205/ローディングテキスト/LoadingText", // menu [Sekiro] + "206/FDP_システムメッセージ_xboxone/GameSystemMessageXboxOne", // menu [DarkSouls3] + "206/LoadingText/LoadingText", // menu [EldenRing] + "206/ローディングタイトル/LoadingTitle", // menu [Sekiro] + "207/TutorialTitle/TutorialTitle", // menu [EldenRing] + "208/TutorialBody/TutorialBody", // menu [EldenRing] + "209/TextEmbedImageName_win64/TextEmbedImageNameWindows", // menu [EldenRing] + "210/ToS_win64/TosWindows", // menu [EldenRing] + "210/アイテム名_dlc1/GoodsName_DLC1", // item [DarkSouls3] + "211/武器名_dlc1/WeaponName_DLC1", // item [DarkSouls3] + "212/防具名_dlc1/ProtectorName_DLC1", // item [DarkSouls3] + "213/アクセサリ名_dlc1/AccessoryName_DLC1", // item [DarkSouls3] + "214/魔法名_dlc1/MagicName_DLC1", // item [DarkSouls3] + "215/NPC名_dlc1/NpcName_DLC1", // item [DarkSouls3] + "216/地名_dlc1/PlaceName_DLC1", // item [DarkSouls3] + "217/アイテム説明_dlc1/GoodsInfo_DLC1", // item [DarkSouls3] + "220/アクセサリ説明_dlc1/AccessoryInfo_DLC1", // item [DarkSouls3] + "221/アイテムうんちく_dlc1/GoodsCaption_DLC1", // item [DarkSouls3] + "222/武器うんちく_dlc1/WeaponCaption_DLC1", // item [DarkSouls3] + "223/防具うんちく_dlc1/ProtectorCaption_DLC1", // item [DarkSouls3] + "224/アクセサリうんちく_dlc1/AccessoryCaption_DLC1", // item [DarkSouls3] + "225/魔法説明_dlc1/MagicInfo_DLC1", // item [DarkSouls3] + "226/魔法うんちく_dlc1/MagicCaption_DLC1", // item [DarkSouls3] + "230/会話_dlc1/TalkMsg_DLC1", // menu [DarkSouls3] + "231/イベントテキスト_dlc1/EventText_DLC1", // menu [DarkSouls3] + "232/FDP_メニューテキスト_dlc1/GameMenuText_DLC1", // menu [DarkSouls3] + "233/FDP_一行ヘルプ_dlc1/GameLineHelp_DLC1", // menu [DarkSouls3] + "235/FDP_システムメッセージ_win64_dlc1/GameSystemMessageWindows_DLC1", // menu [DarkSouls3] + "236/FDP_ダイアログ_dlc1/GameDialogues_DLC1", // menu [DarkSouls3] + "237/FDP_システムメッセージ_ps4_dlc1/GameSystemMessagePS4_DLC1", // menu [DarkSouls3] + "238/FDP_システムメッセージ_xboxone_dlc1/GameSystemMessageXboxOne_DLC1", // menu [DarkSouls3] + "239/血文字_dlc1/BloodMsg_DLC1", // menu [DarkSouls3] + "250/アイテム名_dlc2/GoodsName_DLC2", // item [DarkSouls3] + "251/武器名_dlc2/WeaponName_DLC2", // item [DarkSouls3] + "252/防具名_dlc2/ProtectorName_DLC2", // item [DarkSouls3] + "253/アクセサリ名_dlc2/AccessoryName_DLC2", // item [DarkSouls3] + "254/魔法名_dlc2/MagicName_DLC2", // item [DarkSouls3] + "255/NPC名_dlc2/NpcName_DLC2", // item [DarkSouls3] + "256/地名_dlc2/PlaceName_DLC2", // item [DarkSouls3] + "257/アイテム説明_dlc2/GoodsInfo_DLC2", // item [DarkSouls3] + "260/アクセサリ説明_dlc2/AccessoryInfo_DLC2", // item [DarkSouls3] + "261/アイテムうんちく_dlc2/GoodsCaption_DLC2", // item [DarkSouls3] + "262/武器うんちく_dlc2/WeaponCaption_DLC2", // item [DarkSouls3] + "263/防具うんちく_dlc2/ProtectorCaption_DLC2", // item [DarkSouls3] + "264/アクセサリうんちく_dlc2/AccessoryCaption_DLC2", // item [DarkSouls3] + "265/魔法説明_dlc2/MagicInfo_DLC2", // item [DarkSouls3] + "266/魔法うんちく_dlc2/MagicCaption_DLC2", // item [DarkSouls3] + "270/会話_dlc2/TalkMsg_DLC2", // menu [DarkSouls3] + "271/イベントテキスト_dlc2/EventText_DLC2", // menu [DarkSouls3] + "272/FDP_メニューテキスト_dlc2/GameMenuText_DLC2", // menu [DarkSouls3] + "273/FDP_一行ヘルプ_dlc2/GameLineHelp_DLC2", // menu [DarkSouls3] + "275/FDP_システムメッセージ_win64_dlc2/GameSystemMessageWindows_DLC2", // menu [DarkSouls3] + "276/FDP_ダイアログ_dlc2/GameDialogues_DLC2", // menu [DarkSouls3] + "277/FDP_システムメッセージ_ps4_dlc2/GameSystemMessagePS4_DLC2", // menu [DarkSouls3] + "278/FDP_システムメッセージ_xboxone_dlc2/GameSystemMessageXboxOne_DLC2", // menu [DarkSouls3] + "279/血文字_dlc2/BloodMsg_DLC2", // menu [DarkSouls3] + }; + } +} diff --git a/SoapstoneLib/TestConsoleApp/MapTest.cs b/SoapstoneLib/TestConsoleApp/MapTest.cs new file mode 100644 index 0000000..7b7a263 --- /dev/null +++ b/SoapstoneLib/TestConsoleApp/MapTest.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SoapstoneLib; +using SoapstoneLib.Proto; + +namespace TestConsoleApp +{ + internal class MapTest + { + public static async Task Run(string[] args) + { + // TODO: Add more thorough unit tests here. + // For now, this is just a Hello World type interaction. + SoapstoneClient.Provider provider = SoapstoneClient.GetProvider(KnownServer.DSMapStudio); + if (!provider.TryGetClient(out SoapstoneClient client)) + { + Console.WriteLine($"Server {provider.Server} not found"); + return; + } + ServerInfoResponse info = await client.GetServerInfo(); + Console.WriteLine($"Info response: {info}"); + EditorResource mapResource = new EditorResource { Type = EditorResourceType.Map, Game = FromSoftGame.EldenRing }; + PropertySearch search = PropertySearch.AllOf( + PropertySearch.AnyOf( + new PropertySearch.Condition(PropertyComparisonType.Greater, "EntityID", 0), + new PropertySearch.Condition(PropertyComparisonType.Greater, "EntityGroupIDs", 0)), + new PropertySearch.Condition(PropertyComparisonType.Equal, "Type", "Enemy")); + RequestedProperties props = new RequestedProperties().Add("EntityID").AddNonTrivial("EntityGroupIDs"); + List results = await client.SearchObjects(mapResource, SoulsKey.MsbEntryKey.KeyType, search, props); + foreach (SoulsObject obj in results) + { + Console.WriteLine(obj); + } + // Param field access + EditorResource paramResource = new EditorResource { Type = EditorResourceType.Param, Game = FromSoftGame.EldenRing }; + search = PropertySearch.Of(new PropertySearch.Condition(PropertyComparisonType.Equal, "textId", 7000)); + props = new RequestedProperties().Add("ID", "Name", "textId").AddNonTrivial("isGrayoutForRide", "_invalidProp"); + results = await client.SearchObjects(paramResource, SoulsKey.GameParamRowKey.KeyType, search, props); + foreach (SoulsObject obj in results) + { + Console.WriteLine(obj); + } + } + } +} diff --git a/SoapstoneLib/TestConsoleApp/Program.cs b/SoapstoneLib/TestConsoleApp/Program.cs new file mode 100644 index 0000000..02cbb1a --- /dev/null +++ b/SoapstoneLib/TestConsoleApp/Program.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +namespace TestConsoleApp +{ + internal class Program + { + static void Main(string[] args) + { + if (args.Contains("maptest")) + { + MapTest.Run(args).Wait(); + } + else if (args.Contains("fmgdata")) + { + FmgData.Run(args); + } + } + } +} diff --git a/SoapstoneLib/TestConsoleApp/TestConsoleApp.csproj b/SoapstoneLib/TestConsoleApp/TestConsoleApp.csproj new file mode 100644 index 0000000..fefc6b5 --- /dev/null +++ b/SoapstoneLib/TestConsoleApp/TestConsoleApp.csproj @@ -0,0 +1,13 @@ + + + + Exe + net6.0-windows + + + + + + + +