diff --git a/AboutForm.Designer.cs b/AboutForm.Designer.cs
new file mode 100644
index 0000000..b3ef4ee
--- /dev/null
+++ b/AboutForm.Designer.cs
@@ -0,0 +1,96 @@
+namespace CodeWalker
+{
+ partial class AboutForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutForm));
+ this.OkButton = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ this.label2 = new System.Windows.Forms.Label();
+ this.SuspendLayout();
+ //
+ // OkButton
+ //
+ this.OkButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.OkButton.Location = new System.Drawing.Point(317, 173);
+ this.OkButton.Name = "OkButton";
+ this.OkButton.Size = new System.Drawing.Size(75, 23);
+ this.OkButton.TabIndex = 0;
+ this.OkButton.Text = "Ok";
+ this.OkButton.UseVisualStyleBackColor = true;
+ this.OkButton.Click += new System.EventHandler(this.OkButton_Click);
+ //
+ // label1
+ //
+ this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.label1.Location = new System.Drawing.Point(12, 35);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(380, 164);
+ this.label1.TabIndex = 1;
+ this.label1.Text = resources.GetString("label1.Text");
+ this.label1.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+ //
+ // label2
+ //
+ this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label2.Location = new System.Drawing.Point(12, 9);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(380, 26);
+ this.label2.TabIndex = 2;
+ this.label2.Text = "CodeWalker by dexyfex";
+ this.label2.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+ //
+ // AboutForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(404, 208);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.OkButton);
+ this.Controls.Add(this.label1);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "AboutForm";
+ this.Text = "About CodeWalker by dexyfex";
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button OkButton;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Label label2;
+ }
+}
\ No newline at end of file
diff --git a/AboutForm.cs b/AboutForm.cs
new file mode 100644
index 0000000..4e7c34d
--- /dev/null
+++ b/AboutForm.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace CodeWalker
+{
+ public partial class AboutForm : Form
+ {
+ public AboutForm()
+ {
+ InitializeComponent();
+ }
+
+ private void OkButton_Click(object sender, EventArgs e)
+ {
+ Close();
+ }
+ }
+}
diff --git a/AboutForm.resx b/AboutForm.resx
new file mode 100644
index 0000000..5b28ab9
--- /dev/null
+++ b/AboutForm.resx
@@ -0,0 +1,419 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ A tool for visualising data pertaining to the search for the next paradigm.
+
+Special thanks to:
+
+-- Neodymium -- tgascoigne -- CamxxCore --
+-- Tadden -- Gramz -- Kai -- Vertigo -- HL -- Pouaichh --
+-- Dilapidated -- dav90 -- Neos7 -- Jevi -- sollaholla --
+-- The .White team -- CP -- Kilian -- PNWParksFan --
+
+
+
+
+ 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
+ 6P///ysvjWVqtP///////wAAAAAAAAAAAP////D0/0hPpsDG6////////6y02d7k8////3qAx+/z/f//
+ /wAAAAAAAAAAAAAAAAAAAP///////////////8zT8V5ns1Rcrdzh9f///////////wAAAAAAAAAAAAAA
+ AAAAAP////////7+/6ix3nmBxFthtmdwu09WqbC54/v9//r8//j6//39/wAAAAAAAAAAAOjt/H6I0FJc
+ skpSqHF+wRMahFZhs4iT1AsNc1pgrm52v2RsuO/z/gAAAP////////L2/cLJ7rrD64+V4DY+ozU+mYmU
+ 0X2Hy1hfss7V8urv/PP2/v///wAAAP///+Pp+d/k9////////+Pp/4uR3ysymW14xYOM0fD0/P///+Xq
+ +ri/6Pj6/wAAAOrv/j5DnbS75P////////////X4/+/0/ubr+/r7/////////9rh+hgZhKGo2QAAAPDz
+ /eLn+f////j6/2Nqttrg9////+Hn+P3+//3+/1hescLJ6/////L2/eru/AAAAAAAAAAAAP///8rR70tR
+ p/3+//v8/zY6jNPY7////09WqWpwu////wAAAAAAAAAAAAAAAAAAAAAAAPb4/vr7//////v8/5Wd1eHm
+ +P////v8//T3/wAAAAAAAAAAAAAAAP//AAD8PwAA/D8AAPwDAACAAwAAgAMAAIAHAADABwAAwAEAAMAB
+ AAAAAQAAAAEAAAABAAAAAQAAwAcAAOAPAAAoAAAAQAAAAIAAAAABABgAAAAAAAAwAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7//P3//P3//P3/
+ /f7//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/
+ +fv/+fv/+Pr/+fv/+vv//P3//v//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA/f7/+fr/8/b/7PL/5+3/6e/+9Pf/+vv//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAA/P3/9/r/6O7/cXe1UVaet7z17fL/+Pr//f3/AAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+/z/9Pj/4Oj/NzyCUlOd2dz/6O//9Pf//P3/AAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8vb/2+P9X2OmREGLnqPd
+ 4+v/8vb/+/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8fb/
+ 1N35bXK1JSRtbHGz5O7/8fX/+/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA+vv/8PX/3Ob/U1eaDwtXjZLT4+z/8fX/+/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAA+vv/8fb/2eP+MjR6AAA+c3i34Or/8fX/+/z/AAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8vb/1d/7MS91AAA1UFSS4On/8vb/+/z/AAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ 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
+ /v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/+vz/+fv/+Pr/+Pr/+vv//f3//v//AAAAAAAAAAAAAAAA
+ /v//+fv/9Pf/2+L/SVGtAABsLTaZytL58fX/9/n//f7/AAAAAAAAAAAAAAAA/v7/+/z/9/n/9fj/9vn/
+ 9fj/9vj/+vz//f7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7//f3//f3//f3//v7//v//AAAA
+ AAAAAAAAAAAAAAAAAAAA+/z/9vn/6e//mZ7gTVarr7bp6/H/9fj/+vv//v7/AAAAAAAAAAAAAAAAAAAA
+ /v7//f7/+/z/+/z/+/z//P3//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/f3/+Pr/9fj/6e7/4+n/8fb/9Pf/+Pr//f3/AAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/+fv/+fv/+vv/+Pr/+vv/
+ /P3//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7/
+ /f3//P3//f7//v7//v//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////
+ ///////4D/////////AH////////8Af////////wB/////////AH////////8Af////////wB///////
+ //AH////////8Af////////wB/////////AH////////8AfwP//////wB8Af//+Af/AHgB///wA/8AcA
+ H///AB/wBgAf//8AD/AGAB///wAH8AYAH///AAPwBAAf//8AA/AEAD///wAD8AQAP///AAPwBAB///+A
+ A/AEAP///8AD4AAA////4AcAAAH////wDgAAAf/////8AAAH//////gAAAf/////4AAAAf/////gAAAA
+ /f//+AAAAAAAD//AAAAAAAAH/4AAAAAAAAf/gAAAAAAAB/+AAAAAAAAH/4AAAAAAAAf/gAAAAAAAB/+A
+ AAAAAAAP/4AAAAAAAB//wAAAAABAf/4HwAAAAYAf8APAAAADgA/gA+AAAAMAA8AD8AAABwADgAP8AAAf
+ AAOAA/4AAB8AA4ADAAAAAQADgAIAcA4AgAOABgBwDgBAA4AMAGAMADADwDwAYAwAOAfg+ABgBAAeH//4
+ AEAEAB////gAwAYAH///+ADABgAf///4AcAGAB////gBwAcAH///+APAB4A////8B+AHwH//////4A//
+ ///////gD/////////Af//////////////8=
+
+
+
\ No newline at end of file
diff --git a/App.config b/App.config
new file mode 100644
index 0000000..0ce7d78
--- /dev/null
+++ b/App.config
@@ -0,0 +1,207 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ C:\Program Files (x86)\Steam\SteamApps\common\Grand Theft Auto V
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 4
+
+
+ True
+
+
+ 5
+
+
+ Glokon Marker
+
+
+ Glokon Debug
+
+
+ False
+
+
+ None
+
+
+ True
+
+
+ 100
+
+
+ False
+
+
+ True
+
+
+ 6
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 2147483648
+
+
+ 10
+
+
+ 536870912
+
+
+ 1073741824
+
+
+ 134217728
+
+
+ 1
+
+
+ 0.1
+
+
+ 10
+
+
+ 0.005
+
+
+ 1
+
+
+ Default
+
+
+ DiffuseSampler
+
+
+ Texture coord 1
+
+
+ Installers;_CommonRedist
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+
+
+
+
+
+ Move Forwards: W
+ Move Backwards: S
+ Move Left: A
+ Move Right: D
+ Move Up: R
+ Move Down: F
+ Move Slower / Zoom In: Z
+ Move Faster / Zoom Out: X
+ Toggle Mouse Select: C
+ Toggle Toolbar: T
+ Exit Edit Mode: Q
+ Edit Position: W
+ Edit Rotation: E
+ Edit Scale: R
+
+
+
+
+ True
+
+
+ False
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 15
+
+
+ False
+
+
+ True
+
+
+ Blue
+
+
+ Windows
+
+
+
+
\ No newline at end of file
diff --git a/BinarySearchForm.Designer.cs b/BinarySearchForm.Designer.cs
new file mode 100644
index 0000000..c2aadf5
--- /dev/null
+++ b/BinarySearchForm.Designer.cs
@@ -0,0 +1,625 @@
+namespace CodeWalker
+{
+ partial class BinarySearchForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(BinarySearchForm));
+ this.FileSearchTextRadio = new System.Windows.Forms.RadioButton();
+ this.FileSearchHexRadio = new System.Windows.Forms.RadioButton();
+ this.FileSearchAbortButton = new System.Windows.Forms.Button();
+ this.FileSearchResultsTextBox = new System.Windows.Forms.TextBox();
+ this.FileSearchTextBox = new System.Windows.Forms.TextBox();
+ this.FileSearchFolderBrowseButton = new System.Windows.Forms.Button();
+ this.FileSearchButton = new System.Windows.Forms.Button();
+ this.label2 = new System.Windows.Forms.Label();
+ this.FileSearchFolderTextBox = new System.Windows.Forms.TextBox();
+ this.FolderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
+ this.MainTabControl = new System.Windows.Forms.TabControl();
+ this.SearchRPFTabPage = new System.Windows.Forms.TabPage();
+ this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+ this.RpfSearchOnlyTextBox = new System.Windows.Forms.TextBox();
+ this.RpfSearchOnlyCheckBox = new System.Windows.Forms.CheckBox();
+ this.RpfSearchSaveResultsButton = new System.Windows.Forms.Button();
+ this.RpfSearchIgnoreTextBox = new System.Windows.Forms.TextBox();
+ this.RpfSearchIgnoreCheckBox = new System.Windows.Forms.CheckBox();
+ this.RpfSearchBothDirectionsCheckBox = new System.Windows.Forms.CheckBox();
+ this.RpfSearchCaseSensitiveCheckBox = new System.Windows.Forms.CheckBox();
+ this.RpfSearchHexRadioButton = new System.Windows.Forms.RadioButton();
+ this.RpfSearchTextRadioButton = new System.Windows.Forms.RadioButton();
+ this.RpfSearchResultsListView = new System.Windows.Forms.ListView();
+ this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.RpfSearchAbortButton = new System.Windows.Forms.Button();
+ this.RpfSearchButton = new System.Windows.Forms.Button();
+ this.label3 = new System.Windows.Forms.Label();
+ this.RpfSearchTextBox = new System.Windows.Forms.TextBox();
+ this.ExportCompressCheckBox = new System.Windows.Forms.CheckBox();
+ this.ExportButton = new System.Windows.Forms.Button();
+ this.FileInfoLabel = new System.Windows.Forms.Label();
+ this.ShowLargeFileContentsCheckBox = new System.Windows.Forms.CheckBox();
+ this.DataHexLineCombo = new System.Windows.Forms.ComboBox();
+ this.DataTextRadio = new System.Windows.Forms.RadioButton();
+ this.DataHexRadio = new System.Windows.Forms.RadioButton();
+ this.DataTextBox = new System.Windows.Forms.TextBox();
+ this.SearchFileSystemTab = new System.Windows.Forms.TabPage();
+ this.FileSearchPanel = new System.Windows.Forms.Panel();
+ this.MainStatusStrip = new System.Windows.Forms.StatusStrip();
+ this.StatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
+ this.SaveFileDialog = new System.Windows.Forms.SaveFileDialog();
+ this.MainTabControl.SuspendLayout();
+ this.SearchRPFTabPage.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+ this.splitContainer1.Panel1.SuspendLayout();
+ this.splitContainer1.Panel2.SuspendLayout();
+ this.splitContainer1.SuspendLayout();
+ this.SearchFileSystemTab.SuspendLayout();
+ this.FileSearchPanel.SuspendLayout();
+ this.MainStatusStrip.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // FileSearchTextRadio
+ //
+ this.FileSearchTextRadio.AutoSize = true;
+ this.FileSearchTextRadio.Location = new System.Drawing.Point(162, 30);
+ this.FileSearchTextRadio.Name = "FileSearchTextRadio";
+ this.FileSearchTextRadio.Size = new System.Drawing.Size(48, 19);
+ this.FileSearchTextRadio.TabIndex = 45;
+ this.FileSearchTextRadio.Text = "Text";
+ this.FileSearchTextRadio.UseVisualStyleBackColor = true;
+ //
+ // FileSearchHexRadio
+ //
+ this.FileSearchHexRadio.AutoSize = true;
+ this.FileSearchHexRadio.Checked = true;
+ this.FileSearchHexRadio.Location = new System.Drawing.Point(112, 30);
+ this.FileSearchHexRadio.Name = "FileSearchHexRadio";
+ this.FileSearchHexRadio.Size = new System.Drawing.Size(47, 19);
+ this.FileSearchHexRadio.TabIndex = 44;
+ this.FileSearchHexRadio.TabStop = true;
+ this.FileSearchHexRadio.Text = "Hex";
+ this.FileSearchHexRadio.UseVisualStyleBackColor = true;
+ //
+ // FileSearchAbortButton
+ //
+ this.FileSearchAbortButton.Location = new System.Drawing.Point(420, 27);
+ this.FileSearchAbortButton.Name = "FileSearchAbortButton";
+ this.FileSearchAbortButton.Size = new System.Drawing.Size(75, 23);
+ this.FileSearchAbortButton.TabIndex = 43;
+ this.FileSearchAbortButton.Text = "Abort";
+ this.FileSearchAbortButton.UseVisualStyleBackColor = true;
+ this.FileSearchAbortButton.Click += new System.EventHandler(this.FileSearchAbortButton_Click);
+ //
+ // FileSearchResultsTextBox
+ //
+ this.FileSearchResultsTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.FileSearchResultsTextBox.Location = new System.Drawing.Point(85, 78);
+ this.FileSearchResultsTextBox.Multiline = true;
+ this.FileSearchResultsTextBox.Name = "FileSearchResultsTextBox";
+ this.FileSearchResultsTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.FileSearchResultsTextBox.Size = new System.Drawing.Size(685, 403);
+ this.FileSearchResultsTextBox.TabIndex = 42;
+ this.FileSearchResultsTextBox.WordWrap = false;
+ //
+ // FileSearchTextBox
+ //
+ this.FileSearchTextBox.Location = new System.Drawing.Point(214, 29);
+ this.FileSearchTextBox.Name = "FileSearchTextBox";
+ this.FileSearchTextBox.Size = new System.Drawing.Size(119, 20);
+ this.FileSearchTextBox.TabIndex = 41;
+ this.FileSearchTextBox.Text = "4a03746e";
+ //
+ // FileSearchFolderBrowseButton
+ //
+ this.FileSearchFolderBrowseButton.Location = new System.Drawing.Point(339, 1);
+ this.FileSearchFolderBrowseButton.Name = "FileSearchFolderBrowseButton";
+ this.FileSearchFolderBrowseButton.Size = new System.Drawing.Size(27, 23);
+ this.FileSearchFolderBrowseButton.TabIndex = 40;
+ this.FileSearchFolderBrowseButton.Text = "...";
+ this.FileSearchFolderBrowseButton.UseVisualStyleBackColor = true;
+ this.FileSearchFolderBrowseButton.Click += new System.EventHandler(this.FileSearchFolderBrowseButton_Click);
+ //
+ // FileSearchButton
+ //
+ this.FileSearchButton.Location = new System.Drawing.Point(339, 27);
+ this.FileSearchButton.Name = "FileSearchButton";
+ this.FileSearchButton.Size = new System.Drawing.Size(75, 23);
+ this.FileSearchButton.TabIndex = 39;
+ this.FileSearchButton.Text = "Search";
+ this.FileSearchButton.UseVisualStyleBackColor = true;
+ this.FileSearchButton.Click += new System.EventHandler(this.FileSearchButton_Click);
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(3, 6);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(83, 15);
+ this.label2.TabIndex = 38;
+ this.label2.Text = "Search folder:";
+ //
+ // FileSearchFolderTextBox
+ //
+ this.FileSearchFolderTextBox.Location = new System.Drawing.Point(82, 3);
+ this.FileSearchFolderTextBox.Name = "FileSearchFolderTextBox";
+ this.FileSearchFolderTextBox.Size = new System.Drawing.Size(251, 20);
+ this.FileSearchFolderTextBox.TabIndex = 37;
+ this.FileSearchFolderTextBox.Text = "Compiled944";
+ //
+ // MainTabControl
+ //
+ this.MainTabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.MainTabControl.Controls.Add(this.SearchRPFTabPage);
+ this.MainTabControl.Controls.Add(this.SearchFileSystemTab);
+ this.MainTabControl.Location = new System.Drawing.Point(3, 5);
+ this.MainTabControl.Name = "MainTabControl";
+ this.MainTabControl.SelectedIndex = 0;
+ this.MainTabControl.Size = new System.Drawing.Size(846, 525);
+ this.MainTabControl.TabIndex = 46;
+ //
+ // SearchRPFTabPage
+ //
+ this.SearchRPFTabPage.Controls.Add(this.splitContainer1);
+ this.SearchRPFTabPage.Location = new System.Drawing.Point(4, 22);
+ this.SearchRPFTabPage.Name = "SearchRPFTabPage";
+ this.SearchRPFTabPage.Padding = new System.Windows.Forms.Padding(3);
+ this.SearchRPFTabPage.Size = new System.Drawing.Size(838, 499);
+ this.SearchRPFTabPage.TabIndex = 0;
+ this.SearchRPFTabPage.Text = "Search RPF contents";
+ this.SearchRPFTabPage.UseVisualStyleBackColor = true;
+ //
+ // splitContainer1
+ //
+ this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
+ this.splitContainer1.Location = new System.Drawing.Point(3, 3);
+ this.splitContainer1.Name = "splitContainer1";
+ //
+ // splitContainer1.Panel1
+ //
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchOnlyTextBox);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchOnlyCheckBox);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchSaveResultsButton);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchIgnoreTextBox);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchIgnoreCheckBox);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchBothDirectionsCheckBox);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchCaseSensitiveCheckBox);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchHexRadioButton);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchTextRadioButton);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchResultsListView);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchAbortButton);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchButton);
+ this.splitContainer1.Panel1.Controls.Add(this.label3);
+ this.splitContainer1.Panel1.Controls.Add(this.RpfSearchTextBox);
+ //
+ // splitContainer1.Panel2
+ //
+ this.splitContainer1.Panel2.Controls.Add(this.ExportCompressCheckBox);
+ this.splitContainer1.Panel2.Controls.Add(this.ExportButton);
+ this.splitContainer1.Panel2.Controls.Add(this.FileInfoLabel);
+ this.splitContainer1.Panel2.Controls.Add(this.ShowLargeFileContentsCheckBox);
+ this.splitContainer1.Panel2.Controls.Add(this.DataHexLineCombo);
+ this.splitContainer1.Panel2.Controls.Add(this.DataTextRadio);
+ this.splitContainer1.Panel2.Controls.Add(this.DataHexRadio);
+ this.splitContainer1.Panel2.Controls.Add(this.DataTextBox);
+ this.splitContainer1.Size = new System.Drawing.Size(832, 493);
+ this.splitContainer1.SplitterDistance = 270;
+ this.splitContainer1.TabIndex = 1;
+ //
+ // RpfSearchOnlyTextBox
+ //
+ this.RpfSearchOnlyTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.RpfSearchOnlyTextBox.Enabled = false;
+ this.RpfSearchOnlyTextBox.Location = new System.Drawing.Point(68, 92);
+ this.RpfSearchOnlyTextBox.Name = "RpfSearchOnlyTextBox";
+ this.RpfSearchOnlyTextBox.Size = new System.Drawing.Size(198, 20);
+ this.RpfSearchOnlyTextBox.TabIndex = 66;
+ this.RpfSearchOnlyTextBox.Text = ".ysc, .rel";
+ //
+ // RpfSearchOnlyCheckBox
+ //
+ this.RpfSearchOnlyCheckBox.AutoSize = true;
+ this.RpfSearchOnlyCheckBox.Location = new System.Drawing.Point(9, 94);
+ this.RpfSearchOnlyCheckBox.Name = "RpfSearchOnlyCheckBox";
+ this.RpfSearchOnlyCheckBox.Size = new System.Drawing.Size(53, 19);
+ this.RpfSearchOnlyCheckBox.TabIndex = 65;
+ this.RpfSearchOnlyCheckBox.Text = "Only:";
+ this.RpfSearchOnlyCheckBox.UseVisualStyleBackColor = true;
+ this.RpfSearchOnlyCheckBox.CheckedChanged += new System.EventHandler(this.RpfSearchOnlyCheckBox_CheckedChanged);
+ //
+ // RpfSearchSaveResultsButton
+ //
+ this.RpfSearchSaveResultsButton.Enabled = false;
+ this.RpfSearchSaveResultsButton.Location = new System.Drawing.Point(181, 118);
+ this.RpfSearchSaveResultsButton.Name = "RpfSearchSaveResultsButton";
+ this.RpfSearchSaveResultsButton.Size = new System.Drawing.Size(86, 22);
+ this.RpfSearchSaveResultsButton.TabIndex = 62;
+ this.RpfSearchSaveResultsButton.Text = "Save results...";
+ this.RpfSearchSaveResultsButton.UseVisualStyleBackColor = true;
+ this.RpfSearchSaveResultsButton.Click += new System.EventHandler(this.RpfSearchSaveResultsButton_Click);
+ //
+ // RpfSearchIgnoreTextBox
+ //
+ this.RpfSearchIgnoreTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.RpfSearchIgnoreTextBox.Location = new System.Drawing.Point(68, 68);
+ this.RpfSearchIgnoreTextBox.Name = "RpfSearchIgnoreTextBox";
+ this.RpfSearchIgnoreTextBox.Size = new System.Drawing.Size(198, 20);
+ this.RpfSearchIgnoreTextBox.TabIndex = 59;
+ this.RpfSearchIgnoreTextBox.Text = ".ydr, .ydd, .ytd, .yft, .ybn, .ycd, .awc, .bik";
+ //
+ // RpfSearchIgnoreCheckBox
+ //
+ this.RpfSearchIgnoreCheckBox.AutoSize = true;
+ this.RpfSearchIgnoreCheckBox.Checked = true;
+ this.RpfSearchIgnoreCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+ this.RpfSearchIgnoreCheckBox.Location = new System.Drawing.Point(9, 70);
+ this.RpfSearchIgnoreCheckBox.Name = "RpfSearchIgnoreCheckBox";
+ this.RpfSearchIgnoreCheckBox.Size = new System.Drawing.Size(64, 19);
+ this.RpfSearchIgnoreCheckBox.TabIndex = 58;
+ this.RpfSearchIgnoreCheckBox.Text = "Ignore:";
+ this.RpfSearchIgnoreCheckBox.UseVisualStyleBackColor = true;
+ this.RpfSearchIgnoreCheckBox.CheckedChanged += new System.EventHandler(this.RpfSearchIgnoreCheckBox_CheckedChanged);
+ //
+ // RpfSearchBothDirectionsCheckBox
+ //
+ this.RpfSearchBothDirectionsCheckBox.AutoSize = true;
+ this.RpfSearchBothDirectionsCheckBox.Checked = true;
+ this.RpfSearchBothDirectionsCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+ this.RpfSearchBothDirectionsCheckBox.Location = new System.Drawing.Point(109, 48);
+ this.RpfSearchBothDirectionsCheckBox.Name = "RpfSearchBothDirectionsCheckBox";
+ this.RpfSearchBothDirectionsCheckBox.Size = new System.Drawing.Size(107, 19);
+ this.RpfSearchBothDirectionsCheckBox.TabIndex = 57;
+ this.RpfSearchBothDirectionsCheckBox.Text = "Both directions";
+ this.RpfSearchBothDirectionsCheckBox.UseVisualStyleBackColor = true;
+ //
+ // RpfSearchCaseSensitiveCheckBox
+ //
+ this.RpfSearchCaseSensitiveCheckBox.AutoSize = true;
+ this.RpfSearchCaseSensitiveCheckBox.Location = new System.Drawing.Point(9, 48);
+ this.RpfSearchCaseSensitiveCheckBox.Name = "RpfSearchCaseSensitiveCheckBox";
+ this.RpfSearchCaseSensitiveCheckBox.Size = new System.Drawing.Size(105, 19);
+ this.RpfSearchCaseSensitiveCheckBox.TabIndex = 56;
+ this.RpfSearchCaseSensitiveCheckBox.Text = "Case-sensitive";
+ this.RpfSearchCaseSensitiveCheckBox.UseVisualStyleBackColor = true;
+ //
+ // RpfSearchHexRadioButton
+ //
+ this.RpfSearchHexRadioButton.AutoSize = true;
+ this.RpfSearchHexRadioButton.Location = new System.Drawing.Point(158, 5);
+ this.RpfSearchHexRadioButton.Name = "RpfSearchHexRadioButton";
+ this.RpfSearchHexRadioButton.Size = new System.Drawing.Size(47, 19);
+ this.RpfSearchHexRadioButton.TabIndex = 55;
+ this.RpfSearchHexRadioButton.Text = "Hex";
+ this.RpfSearchHexRadioButton.UseVisualStyleBackColor = true;
+ //
+ // RpfSearchTextRadioButton
+ //
+ this.RpfSearchTextRadioButton.AutoSize = true;
+ this.RpfSearchTextRadioButton.Checked = true;
+ this.RpfSearchTextRadioButton.Location = new System.Drawing.Point(106, 5);
+ this.RpfSearchTextRadioButton.Name = "RpfSearchTextRadioButton";
+ this.RpfSearchTextRadioButton.Size = new System.Drawing.Size(48, 19);
+ this.RpfSearchTextRadioButton.TabIndex = 54;
+ this.RpfSearchTextRadioButton.TabStop = true;
+ this.RpfSearchTextRadioButton.Text = "Text";
+ this.RpfSearchTextRadioButton.UseVisualStyleBackColor = true;
+ this.RpfSearchTextRadioButton.CheckedChanged += new System.EventHandler(this.RpfSearchTextRadioButton_CheckedChanged);
+ //
+ // RpfSearchResultsListView
+ //
+ this.RpfSearchResultsListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.RpfSearchResultsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnHeader1,
+ this.columnHeader2});
+ this.RpfSearchResultsListView.FullRowSelect = true;
+ this.RpfSearchResultsListView.HideSelection = false;
+ this.RpfSearchResultsListView.Location = new System.Drawing.Point(3, 146);
+ this.RpfSearchResultsListView.MultiSelect = false;
+ this.RpfSearchResultsListView.Name = "RpfSearchResultsListView";
+ this.RpfSearchResultsListView.Size = new System.Drawing.Size(263, 347);
+ this.RpfSearchResultsListView.TabIndex = 63;
+ this.RpfSearchResultsListView.UseCompatibleStateImageBehavior = false;
+ this.RpfSearchResultsListView.View = System.Windows.Forms.View.Details;
+ this.RpfSearchResultsListView.VirtualMode = true;
+ this.RpfSearchResultsListView.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.RpfSearchResultsListView_RetrieveVirtualItem);
+ this.RpfSearchResultsListView.SelectedIndexChanged += new System.EventHandler(this.RpfSearchResultsListView_SelectedIndexChanged);
+ //
+ // columnHeader1
+ //
+ this.columnHeader1.Text = "File";
+ this.columnHeader1.Width = 176;
+ //
+ // columnHeader2
+ //
+ this.columnHeader2.Text = "Offset";
+ //
+ // RpfSearchAbortButton
+ //
+ this.RpfSearchAbortButton.Location = new System.Drawing.Point(90, 118);
+ this.RpfSearchAbortButton.Name = "RpfSearchAbortButton";
+ this.RpfSearchAbortButton.Size = new System.Drawing.Size(75, 22);
+ this.RpfSearchAbortButton.TabIndex = 61;
+ this.RpfSearchAbortButton.Text = "Abort";
+ this.RpfSearchAbortButton.UseVisualStyleBackColor = true;
+ this.RpfSearchAbortButton.Click += new System.EventHandler(this.RpfSearchAbortButton_Click);
+ //
+ // RpfSearchButton
+ //
+ this.RpfSearchButton.Location = new System.Drawing.Point(9, 119);
+ this.RpfSearchButton.Name = "RpfSearchButton";
+ this.RpfSearchButton.Size = new System.Drawing.Size(75, 22);
+ this.RpfSearchButton.TabIndex = 60;
+ this.RpfSearchButton.Text = "Search";
+ this.RpfSearchButton.UseVisualStyleBackColor = true;
+ this.RpfSearchButton.Click += new System.EventHandler(this.RpfSearchButton_Click);
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(7, 7);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(104, 15);
+ this.label3.TabIndex = 64;
+ this.label3.Text = "Search in files for:";
+ //
+ // RpfSearchTextBox
+ //
+ this.RpfSearchTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.RpfSearchTextBox.Location = new System.Drawing.Point(3, 23);
+ this.RpfSearchTextBox.Name = "RpfSearchTextBox";
+ this.RpfSearchTextBox.Size = new System.Drawing.Size(263, 20);
+ this.RpfSearchTextBox.TabIndex = 53;
+ //
+ // ExportCompressCheckBox
+ //
+ this.ExportCompressCheckBox.AutoSize = true;
+ this.ExportCompressCheckBox.Location = new System.Drawing.Point(87, 35);
+ this.ExportCompressCheckBox.Name = "ExportCompressCheckBox";
+ this.ExportCompressCheckBox.Size = new System.Drawing.Size(119, 19);
+ this.ExportCompressCheckBox.TabIndex = 112;
+ this.ExportCompressCheckBox.Text = "Compress export";
+ this.ExportCompressCheckBox.UseVisualStyleBackColor = true;
+ //
+ // ExportButton
+ //
+ this.ExportButton.Location = new System.Drawing.Point(6, 31);
+ this.ExportButton.Name = "ExportButton";
+ this.ExportButton.Size = new System.Drawing.Size(75, 23);
+ this.ExportButton.TabIndex = 111;
+ this.ExportButton.Text = "Export...";
+ this.ExportButton.UseVisualStyleBackColor = true;
+ this.ExportButton.Click += new System.EventHandler(this.ExportButton_Click);
+ //
+ // FileInfoLabel
+ //
+ this.FileInfoLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.FileInfoLabel.AutoEllipsis = true;
+ this.FileInfoLabel.Location = new System.Drawing.Point(3, 7);
+ this.FileInfoLabel.Name = "FileInfoLabel";
+ this.FileInfoLabel.Size = new System.Drawing.Size(549, 16);
+ this.FileInfoLabel.TabIndex = 110;
+ this.FileInfoLabel.Text = "[Nothing selected]";
+ //
+ // ShowLargeFileContentsCheckBox
+ //
+ this.ShowLargeFileContentsCheckBox.AutoSize = true;
+ this.ShowLargeFileContentsCheckBox.Location = new System.Drawing.Point(392, 62);
+ this.ShowLargeFileContentsCheckBox.Name = "ShowLargeFileContentsCheckBox";
+ this.ShowLargeFileContentsCheckBox.Size = new System.Drawing.Size(156, 19);
+ this.ShowLargeFileContentsCheckBox.TabIndex = 109;
+ this.ShowLargeFileContentsCheckBox.Text = "Show large file contents";
+ this.ShowLargeFileContentsCheckBox.UseVisualStyleBackColor = true;
+ this.ShowLargeFileContentsCheckBox.CheckedChanged += new System.EventHandler(this.ShowLargeFileContentsCheckBox_CheckedChanged);
+ //
+ // DataHexLineCombo
+ //
+ this.DataHexLineCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.DataHexLineCombo.FormattingEnabled = true;
+ this.DataHexLineCombo.Items.AddRange(new object[] {
+ "8",
+ "16",
+ "32"});
+ this.DataHexLineCombo.Location = new System.Drawing.Point(56, 60);
+ this.DataHexLineCombo.Name = "DataHexLineCombo";
+ this.DataHexLineCombo.Size = new System.Drawing.Size(49, 21);
+ this.DataHexLineCombo.TabIndex = 106;
+ this.DataHexLineCombo.SelectedIndexChanged += new System.EventHandler(this.DataHexLineCombo_SelectedIndexChanged);
+ //
+ // DataTextRadio
+ //
+ this.DataTextRadio.AutoSize = true;
+ this.DataTextRadio.Location = new System.Drawing.Point(135, 61);
+ this.DataTextRadio.Name = "DataTextRadio";
+ this.DataTextRadio.Size = new System.Drawing.Size(48, 19);
+ this.DataTextRadio.TabIndex = 107;
+ this.DataTextRadio.Text = "Text";
+ this.DataTextRadio.UseVisualStyleBackColor = true;
+ //
+ // DataHexRadio
+ //
+ this.DataHexRadio.AutoSize = true;
+ this.DataHexRadio.Checked = true;
+ this.DataHexRadio.Location = new System.Drawing.Point(6, 61);
+ this.DataHexRadio.Name = "DataHexRadio";
+ this.DataHexRadio.Size = new System.Drawing.Size(47, 19);
+ this.DataHexRadio.TabIndex = 105;
+ this.DataHexRadio.TabStop = true;
+ this.DataHexRadio.Text = "Hex";
+ this.DataHexRadio.UseVisualStyleBackColor = true;
+ this.DataHexRadio.CheckedChanged += new System.EventHandler(this.DataHexRadio_CheckedChanged);
+ //
+ // DataTextBox
+ //
+ this.DataTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.DataTextBox.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.DataTextBox.HideSelection = false;
+ this.DataTextBox.Location = new System.Drawing.Point(4, 92);
+ this.DataTextBox.Multiline = true;
+ this.DataTextBox.Name = "DataTextBox";
+ this.DataTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.DataTextBox.Size = new System.Drawing.Size(552, 401);
+ this.DataTextBox.TabIndex = 108;
+ this.DataTextBox.Text = "[Please select a search result]";
+ this.DataTextBox.WordWrap = false;
+ //
+ // SearchFileSystemTab
+ //
+ this.SearchFileSystemTab.Controls.Add(this.FileSearchPanel);
+ this.SearchFileSystemTab.Controls.Add(this.FileSearchResultsTextBox);
+ this.SearchFileSystemTab.Location = new System.Drawing.Point(4, 22);
+ this.SearchFileSystemTab.Name = "SearchFileSystemTab";
+ this.SearchFileSystemTab.Padding = new System.Windows.Forms.Padding(3);
+ this.SearchFileSystemTab.Size = new System.Drawing.Size(838, 499);
+ this.SearchFileSystemTab.TabIndex = 1;
+ this.SearchFileSystemTab.Text = "Search file system";
+ this.SearchFileSystemTab.UseVisualStyleBackColor = true;
+ //
+ // FileSearchPanel
+ //
+ this.FileSearchPanel.Controls.Add(this.FileSearchFolderTextBox);
+ this.FileSearchPanel.Controls.Add(this.FileSearchFolderBrowseButton);
+ this.FileSearchPanel.Controls.Add(this.FileSearchTextRadio);
+ this.FileSearchPanel.Controls.Add(this.FileSearchTextBox);
+ this.FileSearchPanel.Controls.Add(this.FileSearchButton);
+ this.FileSearchPanel.Controls.Add(this.FileSearchHexRadio);
+ this.FileSearchPanel.Controls.Add(this.FileSearchAbortButton);
+ this.FileSearchPanel.Controls.Add(this.label2);
+ this.FileSearchPanel.Location = new System.Drawing.Point(3, 6);
+ this.FileSearchPanel.Name = "FileSearchPanel";
+ this.FileSearchPanel.Size = new System.Drawing.Size(536, 66);
+ this.FileSearchPanel.TabIndex = 46;
+ //
+ // MainStatusStrip
+ //
+ this.MainStatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.StatusLabel});
+ this.MainStatusStrip.Location = new System.Drawing.Point(0, 533);
+ this.MainStatusStrip.Name = "MainStatusStrip";
+ this.MainStatusStrip.Size = new System.Drawing.Size(852, 22);
+ this.MainStatusStrip.TabIndex = 47;
+ this.MainStatusStrip.Text = "statusStrip1";
+ //
+ // StatusLabel
+ //
+ this.StatusLabel.Name = "StatusLabel";
+ this.StatusLabel.Size = new System.Drawing.Size(806, 17);
+ this.StatusLabel.Spring = true;
+ this.StatusLabel.Text = "Initialising...";
+ this.StatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // SaveFileDialog
+ //
+ this.SaveFileDialog.AddExtension = false;
+ //
+ // BinarySearchForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(852, 555);
+ this.Controls.Add(this.MainStatusStrip);
+ this.Controls.Add(this.MainTabControl);
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.Name = "BinarySearchForm";
+ this.Text = "Binary Search - CodeWalker by dexyfex";
+ this.Load += new System.EventHandler(this.BinarySearchForm_Load);
+ this.MainTabControl.ResumeLayout(false);
+ this.SearchRPFTabPage.ResumeLayout(false);
+ this.splitContainer1.Panel1.ResumeLayout(false);
+ this.splitContainer1.Panel1.PerformLayout();
+ this.splitContainer1.Panel2.ResumeLayout(false);
+ this.splitContainer1.Panel2.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+ this.splitContainer1.ResumeLayout(false);
+ this.SearchFileSystemTab.ResumeLayout(false);
+ this.SearchFileSystemTab.PerformLayout();
+ this.FileSearchPanel.ResumeLayout(false);
+ this.FileSearchPanel.PerformLayout();
+ this.MainStatusStrip.ResumeLayout(false);
+ this.MainStatusStrip.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.RadioButton FileSearchTextRadio;
+ private System.Windows.Forms.RadioButton FileSearchHexRadio;
+ private System.Windows.Forms.Button FileSearchAbortButton;
+ private System.Windows.Forms.TextBox FileSearchResultsTextBox;
+ private System.Windows.Forms.TextBox FileSearchTextBox;
+ private System.Windows.Forms.Button FileSearchFolderBrowseButton;
+ private System.Windows.Forms.Button FileSearchButton;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.TextBox FileSearchFolderTextBox;
+ private System.Windows.Forms.FolderBrowserDialog FolderBrowserDialog;
+ private System.Windows.Forms.TabControl MainTabControl;
+ private System.Windows.Forms.TabPage SearchRPFTabPage;
+ private System.Windows.Forms.TabPage SearchFileSystemTab;
+ private System.Windows.Forms.ToolStripStatusLabel StatusLabel;
+ private System.Windows.Forms.Panel FileSearchPanel;
+ private System.Windows.Forms.SplitContainer splitContainer1;
+ private System.Windows.Forms.Button RpfSearchSaveResultsButton;
+ private System.Windows.Forms.TextBox RpfSearchIgnoreTextBox;
+ private System.Windows.Forms.CheckBox RpfSearchIgnoreCheckBox;
+ private System.Windows.Forms.CheckBox RpfSearchBothDirectionsCheckBox;
+ private System.Windows.Forms.CheckBox RpfSearchCaseSensitiveCheckBox;
+ private System.Windows.Forms.RadioButton RpfSearchHexRadioButton;
+ private System.Windows.Forms.RadioButton RpfSearchTextRadioButton;
+ private System.Windows.Forms.ListView RpfSearchResultsListView;
+ private System.Windows.Forms.ColumnHeader columnHeader1;
+ private System.Windows.Forms.ColumnHeader columnHeader2;
+ private System.Windows.Forms.Button RpfSearchAbortButton;
+ private System.Windows.Forms.Button RpfSearchButton;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.TextBox RpfSearchTextBox;
+ private System.Windows.Forms.TextBox RpfSearchOnlyTextBox;
+ private System.Windows.Forms.CheckBox RpfSearchOnlyCheckBox;
+ private System.Windows.Forms.CheckBox ShowLargeFileContentsCheckBox;
+ private System.Windows.Forms.ComboBox DataHexLineCombo;
+ private System.Windows.Forms.RadioButton DataTextRadio;
+ private System.Windows.Forms.RadioButton DataHexRadio;
+ private System.Windows.Forms.TextBox DataTextBox;
+ private System.Windows.Forms.Label FileInfoLabel;
+ private System.Windows.Forms.SaveFileDialog SaveFileDialog;
+ private System.Windows.Forms.CheckBox ExportCompressCheckBox;
+ private System.Windows.Forms.Button ExportButton;
+ public System.Windows.Forms.StatusStrip MainStatusStrip;
+ }
+}
\ No newline at end of file
diff --git a/BinarySearchForm.cs b/BinarySearchForm.cs
new file mode 100644
index 0000000..2ff136c
--- /dev/null
+++ b/BinarySearchForm.cs
@@ -0,0 +1,980 @@
+using CodeWalker.GameFiles;
+using CodeWalker.Properties;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace CodeWalker
+{
+ public partial class BinarySearchForm : Form
+ {
+ private volatile bool InProgress = false;
+ private volatile bool AbortOperation = false;
+
+ private GameFileCache FileCache = null;
+ private RpfManager RpfMan = null;
+
+
+ public BinarySearchForm(GameFileCache cache = null)
+ {
+ FileCache = cache;
+ RpfMan = cache?.RpfMan;
+ InitializeComponent();
+ }
+
+ private void BinarySearchForm_Load(object sender, EventArgs e)
+ {
+ FileSearchFolderTextBox.Text = Settings.Default.CompiledScriptFolder;
+
+ DataHexLineCombo.Text = "16";
+ DataTextBox.SetTabStopWidth(3);
+
+
+ if (RpfMan == null)
+ {
+ Task.Run(() =>
+ {
+ GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key);
+ RpfMan = new RpfManager();
+ RpfMan.Init(GTAFolder.CurrentGTAFolder, UpdateStatus, UpdateStatus, false, false);
+ RPFScanComplete();
+ });
+ }
+ else
+ {
+ RPFScanComplete();
+ }
+ }
+
+ private void UpdateStatus(string text)
+ {
+ try
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(() => { UpdateStatus(text); }));
+ }
+ else
+ {
+ StatusLabel.Text = text;
+ }
+ }
+ catch { }
+ }
+ private void RPFScanComplete()
+ {
+ try
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(() => { RPFScanComplete(); }));
+ }
+ else
+ {
+ StatusLabel.Text = "Ready";
+ //RpfSearchPanel.Enabled = true;
+ }
+ }
+ catch { }
+ }
+
+
+
+
+
+
+
+ private void FileSearchFolderBrowseButton_Click(object sender, EventArgs e)
+ {
+ FolderBrowserDialog.SelectedPath = FileSearchFolderTextBox.Text;
+ DialogResult res = FolderBrowserDialog.ShowDialog();
+ if (res == DialogResult.OK)
+ {
+ FileSearchFolderTextBox.Text = FolderBrowserDialog.SelectedPath;
+ }
+ }
+
+ private void FileSearchButton_Click(object sender, EventArgs e)
+ {
+ string searchtxt = FileSearchTextBox.Text;
+ string searchfolder = FileSearchFolderTextBox.Text;
+ AbortOperation = false;
+
+ if (InProgress) return;
+ if (searchfolder.Length == 0)
+ {
+ MessageBox.Show("Please select a folder...");
+ return;
+ }
+ if (!Directory.Exists(searchfolder))
+ {
+ MessageBox.Show("Please select a valid folder!");
+ return;
+ }
+
+ FileSearchResultsTextBox.Clear();
+
+ byte[] searchbytes1;
+ byte[] searchbytes2;
+ int bytelen;
+
+ if (FileSearchHexRadio.Checked)
+ {
+ try
+ {
+ bytelen = searchtxt.Length / 2;
+ searchbytes1 = new byte[bytelen];
+ searchbytes2 = new byte[bytelen];
+ for (int i = 0; i < bytelen; i++)
+ {
+ searchbytes1[i] = Convert.ToByte(searchtxt.Substring(i * 2, 2), 16);
+ searchbytes2[bytelen - i - 1] = searchbytes1[i];
+ }
+ }
+ catch
+ {
+ MessageBox.Show("Please enter a valid hex string.");
+ return;
+ }
+ }
+ else
+ {
+ bytelen = searchtxt.Length;
+ searchbytes1 = new byte[bytelen];
+ searchbytes2 = new byte[bytelen];
+ for (int i = 0; i < bytelen; i++)
+ {
+ searchbytes1[i] = (byte)searchtxt[i];
+ searchbytes2[bytelen - i - 1] = searchbytes1[i];
+ }
+ }
+
+ FileSearchPanel.Enabled = false;
+
+ InProgress = true;
+
+ Task.Run(() =>
+ {
+
+ FileSearchAddResult("Searching " + searchfolder + "...");
+
+ string[] filenames = Directory.GetFiles(searchfolder);
+
+ int matchcount = 0;
+
+ foreach (string filename in filenames)
+ {
+
+
+ FileInfo finf = new FileInfo(filename);
+ byte[] filebytes = File.ReadAllBytes(filename);
+
+ int hitlen1 = 0;
+ int hitlen2 = 0;
+
+ for (int i = 0; i < filebytes.Length; i++)
+ {
+ byte b = filebytes[i];
+ byte b1 = searchbytes1[hitlen1]; //current test byte 1
+ byte b2 = searchbytes2[hitlen2];
+
+ if (b == b1) hitlen1++; else hitlen1 = 0;
+ if (b == b2) hitlen2++; else hitlen2 = 0;
+
+ if (hitlen1 == bytelen)
+ {
+ FileSearchAddResult(finf.Name + ":" + (i - bytelen));
+ matchcount++;
+ hitlen1 = 0;
+ }
+ if (hitlen2 == bytelen)
+ {
+ FileSearchAddResult(finf.Name + ":" + (i - bytelen));
+ matchcount++;
+ hitlen2 = 0;
+ }
+
+ if (AbortOperation)
+ {
+ FileSearchAddResult("Search aborted.");
+ FileSearchComplete();
+ InProgress = false;
+ return;
+ }
+
+ }
+
+ }
+
+ FileSearchAddResult(string.Format("Search complete. {0} results found.", matchcount));
+ FileSearchComplete();
+ InProgress = false;
+ });
+ }
+
+ private void FileSearchAddResult(string result)
+ {
+ try
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(() => { FileSearchAddResult(result); }));
+ }
+ else
+ {
+ FileSearchResultsTextBox.AppendText(result + "\r\n");
+ }
+ }
+ catch { }
+ }
+
+ private void FileSearchComplete()
+ {
+ try
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(() => { FileSearchComplete(); }));
+ }
+ else
+ {
+ FileSearchPanel.Enabled = true;
+ }
+ }
+ catch { }
+ }
+
+ private void FileSearchAbortButton_Click(object sender, EventArgs e)
+ {
+ AbortOperation = true;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ private List RpfSearchResults = new List();
+ private RpfEntry RpfSelectedEntry = null;
+ private int RpfSelectedOffset = -1;
+ private int RpfSelectedLength = 0;
+
+ private class RpfSearchResult
+ {
+ public RpfFileEntry FileEntry { get; set; }
+ public int Offset { get; set; }
+ public int Length { get; set; }
+
+ public RpfSearchResult(RpfFileEntry entry, int offset, int length)
+ {
+ FileEntry = entry;
+ Offset = offset;
+ Length = length;
+ }
+ }
+ private byte LowerCaseByte(byte b)
+ {
+ if ((b >= 65) && (b <= 90)) //upper case alphabet...
+ {
+ b += 32;
+ }
+ return b;
+ }
+
+ private void RpfSearchAddResult(RpfSearchResult result)
+ {
+ try
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(() => { RpfSearchAddResult(result); }));
+ }
+ else
+ {
+ RpfSearchResults.Add(result);
+ RpfSearchResultsListView.VirtualListSize = RpfSearchResults.Count;
+ }
+ }
+ catch { }
+ }
+
+ private void RpfSearch()
+ {
+ if (InProgress) return;
+ if (!(RpfMan?.IsInited ?? false))
+ {
+ MessageBox.Show("Please wait for the scan to complete.");
+ return;
+ }
+ if (RpfSearchTextBox.Text.Length == 0)
+ {
+ MessageBox.Show("Please enter a search term.");
+ return;
+ }
+
+ string searchtxt = RpfSearchTextBox.Text;
+ bool hex = RpfSearchHexRadioButton.Checked;
+ bool casesen = RpfSearchCaseSensitiveCheckBox.Checked || hex;
+ bool bothdirs = RpfSearchBothDirectionsCheckBox.Checked;
+ string[] ignoreexts = null;
+ string[] onlyexts = null;
+ byte[] searchbytes1;
+ byte[] searchbytes2;
+ int bytelen;
+
+ if (!casesen) searchtxt = searchtxt.ToLowerInvariant(); //case sensitive search in lower case.
+
+ if (RpfSearchIgnoreCheckBox.Checked)
+ {
+ ignoreexts = RpfSearchIgnoreTextBox.Text.Split(',');
+ for (int i = 0; i < ignoreexts.Length; i++)
+ {
+ ignoreexts[i] = ignoreexts[i].Trim();
+ }
+ }
+ if (RpfSearchOnlyCheckBox.Checked)
+ {
+ onlyexts = RpfSearchOnlyTextBox.Text.Split(',');
+ for (int i = 0; i < onlyexts.Length; i++)
+ {
+ onlyexts[i] = onlyexts[i].Trim();
+ }
+ }
+
+ if (hex)
+ {
+ if (searchtxt.Length < 2)
+ {
+ MessageBox.Show("Please enter at least one byte of hex (2 characters).");
+ return;
+ }
+ try
+ {
+ bytelen = searchtxt.Length / 2;
+ searchbytes1 = new byte[bytelen];
+ searchbytes2 = new byte[bytelen];
+ for (int i = 0; i < bytelen; i++)
+ {
+ searchbytes1[i] = Convert.ToByte(searchtxt.Substring(i * 2, 2), 16);
+ searchbytes2[bytelen - i - 1] = searchbytes1[i];
+ }
+ }
+ catch
+ {
+ MessageBox.Show("Please enter a valid hex string.");
+ return;
+ }
+ }
+ else
+ {
+ bytelen = searchtxt.Length;
+ searchbytes1 = new byte[bytelen];
+ searchbytes2 = new byte[bytelen]; //reversed text...
+ for (int i = 0; i < bytelen; i++)
+ {
+ searchbytes1[i] = (byte)searchtxt[i];
+ searchbytes2[bytelen - i - 1] = searchbytes1[i];
+ }
+ }
+
+ RpfSearchTextBox.Enabled = false;
+ RpfSearchHexRadioButton.Enabled = false;
+ RpfSearchTextRadioButton.Enabled = false;
+ RpfSearchCaseSensitiveCheckBox.Enabled = false;
+ RpfSearchBothDirectionsCheckBox.Enabled = false;
+ RpfSearchIgnoreCheckBox.Enabled = false;
+ RpfSearchIgnoreTextBox.Enabled = false;
+ RpfSearchButton.Enabled = false;
+ RpfSearchSaveResultsButton.Enabled = false;
+
+ InProgress = true;
+ AbortOperation = false;
+ RpfSearchResultsListView.VirtualListSize = 0;
+ RpfSearchResults.Clear();
+ uint totfiles = 0;
+ uint curfile = 0;
+ var scannedFiles = RpfMan.AllRpfs;
+ Task.Run(() =>
+ {
+
+ DateTime starttime = DateTime.Now;
+ int resultcount = 0;
+
+ for (int f = 0; f < scannedFiles.Count; f++)
+ {
+ var rpffile = scannedFiles[f];
+ totfiles += rpffile.TotalFileCount;
+ }
+
+
+ for (int f = 0; f < scannedFiles.Count; f++)
+ {
+ var rpffile = scannedFiles[f];
+
+ foreach (var entry in rpffile.AllEntries)
+ {
+ var duration = DateTime.Now - starttime;
+ if (AbortOperation)
+ {
+ UpdateStatus(duration.ToString(@"hh\:mm\:ss") + " - Search aborted.");
+ InProgress = false;
+ RpfSearchComplete();
+ return;
+ }
+
+ RpfFileEntry fentry = entry as RpfFileEntry;
+ if (fentry == null) continue;
+
+ curfile++;
+
+ if (fentry.NameLower.EndsWith(".rpf"))
+ { continue; }
+
+ if (onlyexts != null)
+ {
+ bool ignore = true;
+ for (int i = 0; i < onlyexts.Length; i++)
+ {
+ if (fentry.NameLower.EndsWith(onlyexts[i]))
+ {
+ ignore = false;
+ break;
+ }
+ }
+ if (ignore)
+ { continue; }
+ }
+
+ if (ignoreexts != null)
+ {
+ bool ignore = false;
+ for (int i = 0; i < ignoreexts.Length; i++)
+ {
+ if (fentry.NameLower.EndsWith(ignoreexts[i]))
+ {
+ ignore = true;
+ break;
+ }
+ }
+ if (ignore)
+ { continue; }
+ }
+
+ UpdateStatus(string.Format("{0} - Searching {1}/{2} : {3}", duration.ToString(@"hh\:mm\:ss"), curfile, totfiles, fentry.Path));
+
+ byte[] filebytes = fentry.File.ExtractFile(fentry);
+ if (filebytes == null) continue;
+
+
+ int hitlen1 = 0;
+ int hitlen2 = 0;
+
+ for (int i = 0; i < filebytes.Length; i++)
+ {
+ byte b = casesen ? filebytes[i] : LowerCaseByte(filebytes[i]);
+ byte b1 = searchbytes1[hitlen1]; //current test byte 1
+ byte b2 = searchbytes2[hitlen2];
+
+ if (b == b1) hitlen1++; else hitlen1 = 0;
+ if (hitlen1 == bytelen)
+ {
+ RpfSearchAddResult(new RpfSearchResult(fentry, (i - bytelen), bytelen));
+ resultcount++;
+ hitlen1 = 0;
+ }
+ if (bothdirs)
+ {
+ if (b == b2) hitlen2++; else hitlen2 = 0;
+ if (hitlen2 == bytelen)
+ {
+ RpfSearchAddResult(new RpfSearchResult(fentry, (i - bytelen), bytelen));
+ resultcount++;
+ hitlen2 = 0;
+ }
+ }
+ }
+ }
+ }
+
+ var totdur = DateTime.Now - starttime;
+ UpdateStatus(totdur.ToString(@"hh\:mm\:ss") + " - Search complete. " + resultcount.ToString() + " results found.");
+ InProgress = false;
+ RpfSearchComplete();
+ });
+ }
+ private void RpfSearchComplete()
+ {
+ try
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(() => { RpfSearchComplete(); }));
+ }
+ else
+ {
+ RpfSearchTextBox.Enabled = true;
+ RpfSearchHexRadioButton.Enabled = true;
+ RpfSearchTextRadioButton.Enabled = true;
+ RpfSearchCaseSensitiveCheckBox.Enabled = RpfSearchTextRadioButton.Checked;
+ RpfSearchBothDirectionsCheckBox.Enabled = true;
+ RpfSearchIgnoreCheckBox.Enabled = true;
+ RpfSearchIgnoreTextBox.Enabled = RpfSearchIgnoreCheckBox.Checked;
+ RpfSearchButton.Enabled = true;
+ RpfSearchSaveResultsButton.Enabled = true;
+ }
+ }
+ catch { }
+ }
+
+ private void RpfSearchButton_Click(object sender, EventArgs e)
+ {
+ RpfSearch();
+ }
+
+ private void RpfSearchAbortButton_Click(object sender, EventArgs e)
+ {
+ AbortOperation = true;
+ }
+
+ private void RpfSearchSaveResultsButton_Click(object sender, EventArgs e)
+ {
+ SaveFileDialog.FileName = "SearchResults.txt";
+ if (SaveFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ string fpath = SaveFileDialog.FileName;
+
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine("CodeWalker Search Results for \"" + RpfSearchTextBox.Text + "\"");
+ sb.AppendLine("[File path], [Byte offset]");
+ if (RpfSearchResults != null)
+ {
+ foreach (var r in RpfSearchResults)
+ {
+ sb.AppendLine(r.FileEntry.Path + ", " + r.Offset.ToString());
+ }
+ }
+
+ File.WriteAllText(fpath, sb.ToString());
+
+ }
+ }
+
+ private void RpfSearchIgnoreCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ RpfSearchIgnoreTextBox.Enabled = RpfSearchIgnoreCheckBox.Checked;
+ if (RpfSearchIgnoreCheckBox.Checked)
+ {
+ RpfSearchOnlyCheckBox.Checked = false;
+ }
+ }
+
+ private void RpfSearchOnlyCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ RpfSearchOnlyTextBox.Enabled = RpfSearchOnlyCheckBox.Checked;
+ if (RpfSearchOnlyCheckBox.Checked)
+ {
+ RpfSearchIgnoreCheckBox.Checked = false;
+ }
+ }
+
+ private void RpfSearchTextRadioButton_CheckedChanged(object sender, EventArgs e)
+ {
+ RpfSearchCaseSensitiveCheckBox.Enabled = RpfSearchTextRadioButton.Checked;
+ }
+
+ private void RpfSearchResultsListView_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (RpfSearchResultsListView.SelectedIndices.Count == 1)
+ {
+ var i = RpfSearchResultsListView.SelectedIndices[0];
+ if ((i >= 0) && (i < RpfSearchResults.Count))
+ {
+ var r = RpfSearchResults[i];
+ SelectFile(r.FileEntry, r.Offset + 1, r.Length);
+ }
+ else
+ {
+ SelectFile(null, -1, 0);
+ }
+ }
+ else
+ {
+ SelectFile(null, -1, 0);
+ }
+ }
+
+ private void RpfSearchResultsListView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
+ {
+ var item = new ListViewItem();
+ if (e.ItemIndex < RpfSearchResults.Count)
+ {
+ RpfSearchResult r = RpfSearchResults[e.ItemIndex];
+ item.Text = r.FileEntry.Name;
+ item.SubItems.Add(r.Offset.ToString());
+ item.Tag = r;
+ }
+ e.Item = item;
+ }
+
+
+
+
+
+ private void SelectFile()
+ {
+ SelectFile(RpfSelectedEntry, RpfSelectedOffset, RpfSelectedLength);
+ }
+ private void SelectFile(RpfEntry entry, int offset, int length)
+ {
+ RpfSelectedEntry = entry;
+ RpfSelectedOffset = offset;
+ RpfSelectedLength = length;
+
+ RpfFileEntry rfe = entry as RpfFileEntry;
+ if (rfe == null)
+ {
+ RpfDirectoryEntry rde = entry as RpfDirectoryEntry;
+ if (rde != null)
+ {
+ FileInfoLabel.Text = rde.Path + " (Directory)";
+ DataTextBox.Text = "[Please select a data file]";
+ }
+ else
+ {
+ FileInfoLabel.Text = "[Nothing selected]";
+ DataTextBox.Text = "[Please select a search result]";
+ }
+ return;
+ }
+
+
+ Cursor = Cursors.WaitCursor;
+
+ string typestr = "Resource";
+ if (rfe is RpfBinaryFileEntry)
+ {
+ typestr = "Binary";
+ }
+
+ byte[] data = rfe.File.ExtractFile(rfe);
+
+ int datalen = (data != null) ? data.Length : 0;
+ FileInfoLabel.Text = rfe.Path + " (" + typestr + " file) - " + TextUtil.GetBytesReadable(datalen);
+
+
+ if (ShowLargeFileContentsCheckBox.Checked || (datalen < 524287)) //512K
+ {
+ DisplayFileContentsText(rfe, data, length, offset);
+ }
+ else
+ {
+ DataTextBox.Text = "[Filesize >512KB. Select the Show large files option to view its contents]";
+ }
+
+
+
+ //bool istexdict = false;
+ //if (rfe.NameLower.EndsWith(".ymap"))
+ //{
+ // YmapFile ymap = new YmapFile(rfe);
+ // ymap.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = ymap;
+ //}
+ //else if (rfe.NameLower.EndsWith(".ytyp"))
+ //{
+ // YtypFile ytyp = new YtypFile();
+ // ytyp.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = ytyp;
+ //}
+ //else if (rfe.NameLower.EndsWith(".ymf"))
+ //{
+ // YmfFile ymf = new YmfFile();
+ // ymf.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = ymf;
+ //}
+ //else if (rfe.NameLower.EndsWith(".ymt"))
+ //{
+ // YmtFile ymt = new YmtFile();
+ // ymt.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = ymt;
+ //}
+ //else if (rfe.NameLower.EndsWith(".ybn"))
+ //{
+ // YbnFile ybn = new YbnFile();
+ // ybn.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = ybn;
+ //}
+ //else if (rfe.NameLower.EndsWith(".fxc"))
+ //{
+ // FxcFile fxc = new FxcFile();
+ // fxc.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = fxc;
+ //}
+ //else if (rfe.NameLower.EndsWith(".yft"))
+ //{
+ // YftFile yft = new YftFile();
+ // yft.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = yft;
+ // if ((yft.Fragment != null) && (yft.Fragment.Drawable != null) && (yft.Fragment.Drawable.ShaderGroup != null) && (yft.Fragment.Drawable.ShaderGroup.TextureDictionary != null))
+ // {
+ // ShowTextures(yft.Fragment.Drawable.ShaderGroup.TextureDictionary);
+ // istexdict = true;
+ // }
+ //}
+ //else if (rfe.NameLower.EndsWith(".ydr"))
+ //{
+ // YdrFile ydr = new YdrFile();
+ // ydr.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = ydr;
+ // if ((ydr.Drawable != null) && (ydr.Drawable.ShaderGroup != null) && (ydr.Drawable.ShaderGroup.TextureDictionary != null))
+ // {
+ // ShowTextures(ydr.Drawable.ShaderGroup.TextureDictionary);
+ // istexdict = true;
+ // }
+ //}
+ //else if (rfe.NameLower.EndsWith(".ydd"))
+ //{
+ // YddFile ydd = new YddFile();
+ // ydd.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = ydd;
+ // //todo: show embedded texdicts in ydd's? is this possible?
+ //}
+ //else if (rfe.NameLower.EndsWith(".ytd"))
+ //{
+ // YtdFile ytd = new YtdFile();
+ // ytd.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = ytd;
+ // ShowTextures(ytd.TextureDict);
+ // istexdict = true;
+ //}
+ //else if (rfe.NameLower.EndsWith(".ycd"))
+ //{
+ // YcdFile ycd = new YcdFile();
+ // ycd.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = ycd;
+ //}
+ //else if (rfe.NameLower.EndsWith(".ynd"))
+ //{
+ // YndFile ynd = new YndFile();
+ // ynd.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = ynd;
+ //}
+ //else if (rfe.NameLower.EndsWith(".ynv"))
+ //{
+ // YnvFile ynv = new YnvFile();
+ // ynv.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = ynv;
+ //}
+ //else if (rfe.NameLower.EndsWith("_cache_y.dat"))
+ //{
+ // CacheDatFile cdf = new CacheDatFile();
+ // cdf.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = cdf;
+ //}
+ //else if (rfe.NameLower.EndsWith(".rel"))
+ //{
+ // RelFile rel = new RelFile(rfe);
+ // rel.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = rel;
+ //}
+ //else if (rfe.NameLower.EndsWith(".gxt2"))
+ //{
+ // Gxt2File gxt2 = new Gxt2File();
+ // gxt2.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = gxt2;
+ //}
+ //else if (rfe.NameLower.EndsWith(".pso"))
+ //{
+ // JPsoFile pso = new JPsoFile();
+ // pso.Load(data, rfe);
+ // DetailsPropertyGrid.SelectedObject = pso;
+ //}
+ //else
+ //{
+ // DetailsPropertyGrid.SelectedObject = null;
+ //}
+
+
+ //if (!istexdict)
+ //{
+ // ShowTextures(null);
+ //}
+
+
+ Cursor = Cursors.Default;
+
+
+ }
+
+ private void DisplayFileContentsText(RpfFileEntry rfe, byte[] data, int length, int offset)
+ {
+ if (data == null)
+ {
+ Cursor = Cursors.Default;
+ DataTextBox.Text = "[Error extracting file! " + rfe.File.LastError + "]";
+ return;
+ }
+
+ int selline = -1;
+ int selstartc = -1;
+ int selendc = -1;
+
+ if (DataHexRadio.Checked)
+ {
+ int charsperln = int.Parse(DataHexLineCombo.Text);
+ int lines = (data.Length / charsperln) + (((data.Length % charsperln) > 0) ? 1 : 0);
+ StringBuilder hexb = new StringBuilder();
+ StringBuilder texb = new StringBuilder();
+ StringBuilder finb = new StringBuilder();
+
+ if (offset > 0)
+ {
+ selline = offset / charsperln;
+ }
+ for (int i = 0; i < lines; i++)
+ {
+ int pos = i * charsperln;
+ int poslim = pos + charsperln;
+ hexb.Clear();
+ texb.Clear();
+ hexb.AppendFormat("{0:X4}: ", pos);
+ for (int c = pos; c < poslim; c++)
+ {
+ if (c < data.Length)
+ {
+ byte b = data[c];
+ hexb.AppendFormat("{0:X2} ", b);
+ if (char.IsControl((char)b))
+ {
+ texb.Append(".");
+ }
+ else
+ {
+ texb.Append(Encoding.ASCII.GetString(data, c, 1));
+ }
+ }
+ else
+ {
+ hexb.Append(" ");
+ texb.Append(" ");
+ }
+ }
+
+ if (i == selline) selstartc = finb.Length;
+
+ finb.AppendLine(hexb.ToString() + "| " + texb.ToString());
+
+ if (i == selline) selendc = finb.Length - 1;
+ }
+
+ DataTextBox.Text = finb.ToString();
+ }
+ else
+ {
+
+ string text = Encoding.UTF8.GetString(data);
+
+
+ DataTextBox.Text = text;
+
+ if (offset > 0)
+ {
+ selstartc = offset;
+ selendc = offset + length;
+ }
+ }
+
+ if ((selstartc > 0) && (selendc > 0))
+ {
+ DataTextBox.SelectionStart = selstartc;
+ DataTextBox.SelectionLength = selendc - selstartc;
+ DataTextBox.ScrollToCaret();
+ }
+
+ }
+
+ private void ExportButton_Click(object sender, EventArgs e)
+ {
+ if (InProgress) return;
+ if (!(RpfMan?.IsInited ?? false))
+ {
+ MessageBox.Show("Please wait for the scan to complete.");
+ return;
+ }
+
+ RpfFileEntry rfe = RpfSelectedEntry as RpfFileEntry;
+ if (rfe == null)
+ {
+ MessageBox.Show("Please select a file to export.");
+ return;
+ }
+
+ SaveFileDialog.FileName = rfe.Name;
+ if (SaveFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ string fpath = SaveFileDialog.FileName;
+
+ byte[] data = rfe.File.ExtractFile(rfe);
+
+
+ if (ExportCompressCheckBox.Checked)
+ {
+ data = ResourceBuilder.Compress(data);
+ }
+
+
+ RpfResourceFileEntry rrfe = rfe as RpfResourceFileEntry;
+ if (rrfe != null) //add resource header if this is a resource file.
+ {
+ data = ResourceBuilder.AddResourceHeader(rrfe, data);
+ }
+
+ if (data == null)
+ {
+ MessageBox.Show("Error extracting file! " + rfe.File.LastError);
+ return;
+ }
+
+ try
+ {
+
+ File.WriteAllBytes(fpath, data);
+
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Error saving file! " + ex.ToString());
+ }
+
+ }
+ }
+
+ private void DataHexRadio_CheckedChanged(object sender, EventArgs e)
+ {
+ SelectFile();
+ }
+
+ private void DataHexLineCombo_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ SelectFile();
+ }
+
+ private void ShowLargeFileContentsCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ SelectFile();
+ }
+ }
+}
diff --git a/BinarySearchForm.resx b/BinarySearchForm.resx
new file mode 100644
index 0000000..963f422
--- /dev/null
+++ b/BinarySearchForm.resx
@@ -0,0 +1,418 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 182, 17
+
+
+ 317, 17
+
+
+
+
+ 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
+ 6P///ysvjWVqtP///////wAAAAAAAAAAAP////D0/0hPpsDG6////////6y02d7k8////3qAx+/z/f//
+ /wAAAAAAAAAAAAAAAAAAAP///////////////8zT8V5ns1Rcrdzh9f///////////wAAAAAAAAAAAAAA
+ AAAAAP////////7+/6ix3nmBxFthtmdwu09WqbC54/v9//r8//j6//39/wAAAAAAAAAAAOjt/H6I0FJc
+ skpSqHF+wRMahFZhs4iT1AsNc1pgrm52v2RsuO/z/gAAAP////////L2/cLJ7rrD64+V4DY+ozU+mYmU
+ 0X2Hy1hfss7V8urv/PP2/v///wAAAP///+Pp+d/k9////////+Pp/4uR3ysymW14xYOM0fD0/P///+Xq
+ +ri/6Pj6/wAAAOrv/j5DnbS75P////////////X4/+/0/ubr+/r7/////////9rh+hgZhKGo2QAAAPDz
+ /eLn+f////j6/2Nqttrg9////+Hn+P3+//3+/1hescLJ6/////L2/eru/AAAAAAAAAAAAP///8rR70tR
+ p/3+//v8/zY6jNPY7////09WqWpwu////wAAAAAAAAAAAAAAAAAAAAAAAPb4/vr7//////v8/5Wd1eHm
+ +P////v8//T3/wAAAAAAAAAAAAAAAP//AAD8PwAA/D8AAPwDAACAAwAAgAMAAIAHAADABwAAwAEAAMAB
+ AAAAAQAAAAEAAAABAAAAAQAAwAcAAOAPAAAoAAAAQAAAAIAAAAABABgAAAAAAAAwAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7//P3//P3//P3/
+ /f7//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/
+ +fv/+fv/+Pr/+fv/+vv//P3//v//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA/f7/+fr/8/b/7PL/5+3/6e/+9Pf/+vv//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAA/P3/9/r/6O7/cXe1UVaet7z17fL/+Pr//f3/AAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+/z/9Pj/4Oj/NzyCUlOd2dz/6O//9Pf//P3/AAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8vb/2+P9X2OmREGLnqPd
+ 4+v/8vb/+/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8fb/
+ 1N35bXK1JSRtbHGz5O7/8fX/+/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA+vv/8PX/3Ob/U1eaDwtXjZLT4+z/8fX/+/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAA+vv/8fb/2eP+MjR6AAA+c3i34Or/8fX/+/z/AAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8vb/1d/7MS91AAA1UFSS4On/8vb/+/z/AAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8fb/2OL+NjZ7AAArX2Ok
+ 4uz/8fX/+/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8fb/
+ 2eP/LjJ1DAxKfYTE4Or/8fX/+/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//v7//f7//f7//v7//v//
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA+vv/8PX/3OX/gILIR0eVeoHC3eb/8fX/+/z/AAAAAAAAAAAAAAAAAAAA/v7//P3/+fv/+Pr/
+ +Pr/+Pr/+vv//P3//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7//P3/+vv/+vv/+/z//f3//v7/AAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAA+vv/8PX/2eP9ZWeqHx1obnOz4Or/8fX/+/z/AAAAAAAAAAAAAAAA/v7/
+ +/z/9fj/8vb/8PX/7vT/8fb/9fj/+fr//f7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v///P3/+Pr/9fj/9fj/9Pj/9Pf/9vn/+/z//v7/
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8fb/2eP9ODp9AAA5jZDQ5O7/8PX/+/z/AAAA
+ AAAAAAAA/v7/+/z/9Pf/7fP/5u//wsz6j5XfuMDx7fL/9vn//P3/AAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/f7/+Pr/8/b/5+3/2eH/2uP/
+ 5u3/7fP/8/b/+vv//f7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8PX/3ef/U1ebBgVKio/O
+ 4uz/8fX/+/z/AAAAAAAA/v///P3/9fj/7fP/4uv/hIzZHSWPAABmU1i14ub/9/r/+/z/AAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/P3/9Pf/
+ 7/X/09z/TlSzNzWYj5bh5O7/6/L/8vb/+fv//f7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8fX/
+ 2eP/QUWIEhBZbnSz3uj/8fb/+/z/AAAAAAAA/f7/+Pr/7/T/6PH/iI7cAABvAABqAABncXjK6O//9fj/
+ +/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA+/z/8/f/2uD/Z27EAABnAABiBgl4jJTd5vD/6O//8vX/+fv//f7/AAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA+vv/8fb/2OP/Mjd6AQE6ZGup4er/8fX/+/z/AAAAAAAA+vz/8fX/6/T/xM/8ExyJAABwAABu
+ GySRxc387fT/9ff//P3/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAA+vz/8/f/1Nr/MzqhAABhAxOBAARyBgp5jpLg5Oz/7PP/9Pf/+vz//v7/
+ AAAAAAAAAAAAAAAAAAAAAAAA+vv/8fb/2eP/KCtvBwZOjJHS4Or/8fX/+/z/AAAA/f7/9/n/7fP/3+j/
+ UFq3AABtAAZ3BAh6mZ/n5vD/7vP/+Pr//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+/z/9Pj/6e//sbb1KzWcAABwBhaBAAFyAgp6fITR
+ 1d777/T/+Pr//f7/AAAAAAAAAAAAAAAAAAAAAAAA+vv/8PX/3+j/WF2hBglTnaTj5O3/8PX/+/z/AAAA
+ /P3/9Pf/6vL/k5riAAByAAR0AABrY2vE4ur/6vH/9ff//P3/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/f3/9/n/7fL/5O3/ytX/RU6w
+ AABpAA5+AABuAABnhord6e7/+fv//f7/AAAAAAAAAAAAAAAAAAAAAAAA+vv/7/T/3+j/k5jbT1KdgYjJ
+ 3uf+8fX/+/z/AAAA+/z/9fn/4ef/NDqhAABnAABrJjCU0Nn/5/D/8fX/+vv//v7/AAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7/+/z/
+ 9vn/7vP/6vP/ztb/O0CmAABpAABrQkuoxMn57PH/+Pr//f7/AAAAAAAAAAAAAAAAAAAAAAAA+vv/8PX/
+ 2+X/en/CUFGak5nY3+j/8fX//P3/AAAA/P3/9fj/4en/i5DbNT2hIyuTpqzv4uz/7vP/9/n//f7/AAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAA/v7//P3/9vn/7/P/6vL/ytH/X2i9XWi7wsf/6e//8/f/+Pr//v7/AAAAAAAAAAAAAAAA
+ AAAAAAAA+vv/8PX/3OX/WF2hW1ylvMD+3uf/8PX/+/z/AAAA/f7/9vn/7fP/4uj/j5Pgf4LV3+X/6fD/
+ 9Pf//P3/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v///P3/+Pr/8vX/7fP/5+//5u7/6vD/8PT/9vn//P3//v7/
+ AAAAAAAAAAAAAAAAAAAA/f7/9/n/7fP/0tz9LDJzNjh/nqTk2uT/7fL/9/n//f7//f7/+fv/8/b/7PL/
+ 3eX/zM//5ev/9fj/+fv//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v///f3/+vv/9/n/9vn/9fj/9vn/
+ +fr//P3//v7/AAAAAAAAAAAA/v///f7/+vv/9vn/7/T/5vD/2Ob/VFubERNdoajk4u//5O7/7vP/9vj/
+ +fr/+vv/+Pr/9fj/9Pj/9fj/9fj/+Pr//P3/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v///v7/
+ /f7//P3//P3//f3//v7//v//AAAAAAAAAAAA/f7/+vz/9vn/8fX/7vT/5O3/3eb/z9n/cHjICxN5d37L
+ z9n/2eP/5O3/6/L/8PT/9Pf/9/n/+vv/+vv/+/z//P3//f3//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/P3/+Pr/8/b/7vT/6vL/z9r+jZjeQUeq
+ IiuQCBN3AAFrBRB8Nj2iUViym6XlydH/4+z/6/L/8PT/9/n/+/z//f7//v//AAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/f3/9/n/8fX/6/L/3uf/
+ mKTkLzibAABoAAB0Fx+HDBh7FSGDAg16AABYAABlCBB/Ji2UhYza1+D/6PL/7fL/9Pf/+vv//f7/AAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/9/n/
+ 8PT/7PT/z9j/XmO+AABtAABcMDSXoajsu8X7VV+5hYzblZ/fTVSxFSKMAABkAABnAAN2Qkmpsbrz5e3/
+ 6vH/8fX/+Pr//P3//v//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAA/P3/9/n/8PX/7PT/vcn3LTOZAABaAgR1ZWzD0Nf/5vL/1OP/l53lzs3/6fP/4+7/sLzwZ23CBxSD
+ AABnAABlHiaSmqHo3+j/5+//7/T/9vn//P3//v7/AAAAAAAAAAAAAAAAAAAAAAAA/v//AAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7/
+ /v7//v7//v7//f7/+/z/9vj/7vP/7PX/tcLzEBeGAABkPEWlqLPt2eX/4e7/3On/uMX1gofVe3vPhYzY
+ z93+5/X/4e3/lJ3gHiOPAABtAABqChiEbHLIytD/5/D/7PL/8/f/+Pr/+fr/+Pr/+Pr/+Pr/+Pr/+Pr/
+ +Pr/+fv/+vv/+/z//f7//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ /v7//f7/+/z/+fv/9/n/9vj/9fj/9Pf/8fX/7PL/4uv/l6HgDhF7AAN4iZDe0d7/3uz/4vD/w83/VVm3
+ ICiSAAFyAABlAABwaHTD1N//2un/3er/w838ZW3BEyOJJzKVAQ16NDmfwsn75fD/5u7/7PL/7vP/7fP/
+ 7fP/7fL/7fP/7vP/7/T/8fb/9Pj/9vn/+fr//f3//v//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAA/v7//P3/+Pr/9Pf/8fX/7vT/7PL/6/L/6fH/5u7/6vX/tsD0CQx4AAFwkZvi7ff/4vD/
+ 4fD/z9j/OkGlAABiAABwBxWAAAt7BBN+P0uofYLUztb/4O7/6fb/6fP/qa7xQkyoBg56AABqMjugx8/+
+ 5fH/4Ov/4On/3uj/3eb/3+j/3uj/1+L/0d3/1d7/3+f/7fL/9vj/+vz//v7/AAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAA/f7/+fr/8/f/6/L/2d//v8j6vcf5ucP1wMv8wM3+vMj6PkqoAABo
+ UF25usP7tsPyvsr6sLrwQ0utAABqAAV1OUameIDRKDWZAAd2GyeOLDecmaHntsL0pbLom6riq7LzUlu0
+ AANzBhR/AAZ0NT+ja3bBY2i/XGG6UViyWl65XGG7XGC6TVWvQU6pPkalODygqK7p8vb/+vz//v7/AAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/P3/9/n/7/T/wcj2R0ysExeFERmGDxuIFB6K
+ FBqICxSEAABsAAByDBiDCRSBBRCADhaFCRODAAh4AxF/AAl4CxeDHSaPAAp6AAN0AA19AAd3CBOBEBqH
+ BhGBAAh5AABwAAByAAh5BhSCAxWCAABsAABvAABlAABnAABxAABjAABmAABhAABdAABYAABhCAt/q7Lr
+ 8/f/+vv//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/P3/+fv/3uT/SE2vAABn
+ CBB/GiCMLzmfLTWcGByJFRyKGCOOMj2gHymRDxiGGyOPLDCXBRF/AAh3BhaCEyKMICqTKC2WNDqfIzCV
+ Awx6Eh+JHiaPAAR3AAZ5CxSDICWQX2q7Q1CqAA1+AAFxDxuHiZTbVGC4dHnQnabrTVqzY23EUV62Slau
+ LjaZXWm9sLjz5ez/9vn/+fv//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/P3/
+ +Pv/4+n+e4LPfoPVpqv2vsf/zNX/zdb/xtH/v8v8pK7spKfysLb3vcr4ws784ej/hI/YAAZ1AAJzVF25
+ yM//3Of/5+//i5LcAABpMzyfp6vxoKznlqHhqbbtx9H/8fz/kpvfAABiAABph4zc5PD/2OP/193/3un/
+ 1+D/2OH/1+D/0Nr/zNL/3+j/6/L/7/T/9vn//P3//v//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAA/f7/+Pr/9Pf/6vD/5u3/3+b/4uv/6PD/5+//5O3/5/P/sL3sXmS7mZzoz9f/3+z/4e//
+ mKLiEiKKCBF/KTWZr7T06/f/3ev/VF2zChSBipPcz9v+4u7/3ur/3ev/5/X/qrPrISmSDRJ2Xmq/3ur/
+ 4uv/6vH/7fP/7fL/7/T/7vP/7fP/7fP/8PX/8fX/9Pf/+Pr/+/z//v7/AAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/+Pr/9vn/9Pf/8vb/8vb/8/b/9Pf/7/T/6/L/tL/ubXLH
+ en/Ti43gqavy0t3/nafjMj6fJzaaAAV1GyeOYmW7Nz6fAABgNj6i1N//3uz/2uX/3Oj/5PH/wcj7FR2J
+ AAN0gong0tr/6fH/7/P/9vj/+Pr/+fv/+fv/+Pr/+Pr/+Pr/+fv/+vv//P3//f7//v//AAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7//P3/+/z/+/z/+/z//f3//f7/
+ +fv/8fX/5Oz/jpbfc3jObnXLcXfOk5rks7b4iY3dR1KvDhuEAABoAABlEBV9U12ytcD13Or/3en/3ej/
+ 1eL/q7fvGR+MKDKZbnnNxc/76PD/8fX/+fr//f7//v//AAAA/v7//f7//f3//P3//f3//f7//v//AAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7//P3//P3//f7//v7/AAAA
+ AAAAAAAAAAAAAAAA/f7/9vn/7/T/yNH5lJrleoDVmZ3pmpzpc3nPfoTWf4bYVFy3HSaLZ3PGsrb8v8r8
+ y9n9q7jre4LRf4fUgIvXAwZ1AABrhYjb0NX/6PH/8PX/+Pr//f7/AAAAAAAA/v///f3/+vv/+Pr/9/r/
+ 9/n/+Pr/+/z//f7//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v///f7/+/z/+fr/9vj/9/n/
+ +vz/+vv/+/z//v7/AAAAAAAAAAAAAAAA/v7/+vz/8/f/7PL/2uT/t8H1srP6vcH+nKTnSlOxV2C7TVaz
+ WGS8QUqmSlSuSFOtR1GtbXTKVl23ARB5AAh2AABnd33P3eP/4ur/7/T/9/n//P3/AAAAAAAAAAAA/P3/
+ 9/n/8vb/7PH/6fD/7PL/7vP/8vb/9vn/+/z//f7/AAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7/+/z/+Pr/
+ 8/b/7/T/8Pb/6vH/3eP97vL++fr//P3/AAAAAAAAAAAAAAAAAAAA/f7/+vv/9fj/7/T/5+//z9f+t7v4
+ uLn9Z2zFLzucFCGIMz6gGCCMAAd4AAl2Dx2EER+GXWK8c3XLKzKXd4LP4er/6/L/8PX/9/n//P3//v//
+ AAAAAAAA/v7/+fv/8/b/7PP/y9H/i4/erLbt4er/5e3/7fP/8/b/+fv//f3//v7/AAAAAAAAAAAAAAAA
+ /v7/+/z/9vj/8PT/6/L/3+n/x9H9aHTAZGvG3+b9+Pr/+/z/AAAAAAAAAAAAAAAAAAAAAAAA/v7/+/z/
+ +Pr/8vb/6/H/3OX+wMn4maDmdHrPWGG6T1a1eoHWcHfOTlayUlq1SlKubHjAxMj/0dn/4+v/7PL/8vb/
+ +Pr//P3//v7/AAAAAAAAAAAA/f7/+fr/7vP/xsv5YGXAHymRKjKYYWS9rbLz4u3/6/P/8vb/+fr//f7/
+ AAAAAAAAAAAA/v//+/z/9vj/7fL/5e3/xs7/Y23BIiiSAABeLTab3+b/9/r/+/z/AAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAA/f7/+vz/9vj/8PX/6vH/3eb/ydL8xM/6uMPyt733w8j/zNb/1Nz/3OT/4uz/5u7/
+ 7fP/8vb/9vj/+vz//f7/AAAAAAAAAAAAAAAAAAAA/f7/+fv/7vP/jpHiAAJ1CxaBER6GAABoFRmGbXbH
+ 0Nf/7PL/9fj//P3/AAAAAAAAAAAA/v7/+fv/8/f/4Of/hYvbKDGZAABuAABdAAZyi5La5+7/9vn/+/z/
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/+fv/9ff/8vb/7/X/7fP/6/L/5u3/5ez/6fD/
+ 7PP/7/T/8fX/9Pf/9/n/+vv//P3//v7//v//AAAAAAAAAAAAAAAAAAAA/v7/+fv/8fb/2eH9fIbQExqH
+ AABrAAp6AAFyAABwS0+uztX39vn/+vz/AAAAAAAAAAAA/f7/+Pr/8ff/qbLpAABrAABhAABwDBWAfobX
+ 5e3/8PX/9vn//f3/AAAAAAAA/v///f7/+/z/+vv/+vv/+vz//P3//v7//v///v7//P3/+vz/+Pr/9/n/
+ 9vj/9vj/9vj/9vj/9/n/+fr/+/z//P3//f7//v7//f7//P3/+/z/+vz/+/z//P3//v7/AAAA/v7/+/z/
+ 9fj/7/T/5/H/uML1U1e1AAh5AABuAABvMjmdv8bz9vr/+vv/AAAAAAAAAAAA/f7/+fv/7/T/iY7aDxSA
+ GiONa3XHsr7w4Oj/6/H/9Pf/+vz//v7/AAAA/v///P3/+Pr/9Pf/8/f/9fj/9fj/9vn/+/z//v7/AAAA
+ AAAAAAAA/v7//f7//P3/+/z/+/z//P3//f7//v//AAAAAAAAAAAA/v7/+/z/9/n/9vn/9vn/9Pj/9vn/
+ +/z//v7/AAAA/f7/+vz/9fj/7/T/6vL/3ef/i5PbGRqJBQl5jJbZ6vH/9Pj/+/z/AAAAAAAAAAAA/f7/
+ +fv/8fT/1Nn9t7/0wcr54er/7fT/8fX/9fj/+vv//f7/AAAAAAAA/f3/+Pr/8PT/6/L/3uX/ztb/5Or/
+ 8/f/+Pr//f7/AAAAAAAAAAAA/f7/+vz/+Pr/+fv/+fv/+vv//f3//v//AAAAAAAAAAAA/P3/9/n/7vL/
+ 193/ztf/5u3/7vP/9Pf/+/z//v7/AAAA/v7//P3/+Pr/8fX/7PP/5/D/sLfxoKnk4+r/8vf/9/n//f3/
+ AAAAAAAAAAAA/v7/+/z/9vn/9Pf/8vb/8fb/8fX/9Pf/+Pr//P3//v7/AAAAAAAA/v7/+vv/8vb/5+7/
+ y9H/WWO9KSmSkZXj6vD/+Pv//P3/AAAAAAAA/f7/+Pr/9fj/8vb/6O7/7vP/9fj/+Pr//f7/AAAAAAAA
+ /v//+vv/8vb/7PP/hYraKiqKlp7i6PD/7fP/9ff/+/z//v7/AAAAAAAA/f7/+vv/9ff/8fX/8PX/8vb/
+ 8/f/9vn/+/z//v7/AAAAAAAAAAAAAAAA/f7/+/z/+vv/+fr/+fr/+vv//P3//v7/AAAAAAAAAAAAAAAA
+ /P3/9fj/7PL/1d7/RUysAABhAABlg4ja6/D/+Pr//P3/AAAAAAAA+/z/9fj/6e7/2eD/h4/bnaXg7PH/
+ 9fj/+/z/AAAAAAAA/v7/+Pr/8PX/y9X1JDGVAABaERWDoKnp6PH/7vP/9/n//P3/AAAAAAAAAAAA/v7/
+ /P3/+vv/+fv/+fv/+vv//P3//v7/AAAAAAAAAAAAAAAAAAAAAAAA/v7//v7//v7//v7//v//AAAAAAAA
+ AAAAAAAAAAAA/v7/+fv/8PX/7PX/ipPdAABsAABlQ1Cp3Ob/7vP/9/n//f7/AAAAAAAA+fv/9Pj/yNH5
+ Ule2DBJ8Ljie0df+8fb/+fv//v7/AAAA/v7/+Pr/7/X/hY3YAABxAAl7AABuEBaEs7nz6fH/8fX/+vv/
+ /v7/AAAAAAAAAAAAAAAA/v///v7//v7//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAA/f3/9vn/7PL/0tn/LzidAQFsAAB0iZHb6vP/8PT/+fv//v//AAAA
+ /v7/+Pr/8vf/r7rqAAV4AABdPUen1N//7PL/9vn//f7/AAAA/v7/+fr/7/T/yc75S1G0AABrARKAAABp
+ Qker0df/7fP/9/n//f7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/P3/9/n/5+7/cXXNAAd2AABuMDebzdT97PL/
+ 9vj//P3/AAAAAAAA/v7/9/n/7/X/tL/uFCCLAABqHSqRvcf46fD/9Pf//f3/AAAAAAAA+vv/8vX/6vH/
+ yM3+JC2XAABtAAV2Agx9q7Ly7vT/9vn//f7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/P3/9/r/4uj/WWO1AAVx
+ KTaYu8T07fT/8vb/+vv//v7/AAAAAAAA/v7/9/n/7vX/vsn1Iy2SAABrAQ99mp/o6PD/9Pf//P3/AAAA
+ AAAA/P3/9/n/7vP/6fL/s7z2DBB/AABeQ0uttrr56e7/+Pr//f7/AAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/P3/
+ +fv/4ef6g4zNbXfFw8v27fT/8vb/+Pr//f3/AAAAAAAAAAAA/v7/9/n/7vT/yNL7MjucAABtBxF/nKLo
+ 6fH/9Pf//P3/AAAAAAAA/v7/+/z/9fj/7fL/6/T/jZXbLzScrrP14en/7fL/+fv//v7/AAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAA/f7/+vz/8PP91dr34+f/8vb/8/f/9/r//P3//v//AAAAAAAAAAAA/v7/+Pr/8PX/1N3/
+ QUqmAQRxBQ98m6Dm7PL/9fj//P3/AAAAAAAAAAAA/v7/+/z/9ff/8PX/5ez/ytH94ej/8vb/9vj/+/z/
+ /v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/+vz/+fv/+Pr/+Pr/+vv//f3//v//AAAAAAAAAAAAAAAA
+ /v//+fv/9Pf/2+L/SVGtAABsLTaZytL58fX/9/n//f7/AAAAAAAAAAAAAAAA/v7/+/z/9/n/9fj/9vn/
+ 9fj/9vj/+vz//f7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7//f3//f3//f3//v7//v//AAAA
+ AAAAAAAAAAAAAAAAAAAA+/z/9vn/6e//mZ7gTVarr7bp6/H/9fj/+vv//v7/AAAAAAAAAAAAAAAAAAAA
+ /v7//f7/+/z/+/z/+/z//P3//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/f3/+Pr/9fj/6e7/4+n/8fb/9Pf/+Pr//f3/AAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/+fv/+fv/+vv/+Pr/+vv/
+ /P3//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7/
+ /f3//P3//f7//v7//v//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////
+ ///////4D/////////AH////////8Af////////wB/////////AH////////8Af////////wB///////
+ //AH////////8Af////////wB/////////AH////////8AfwP//////wB8Af//+Af/AHgB///wA/8AcA
+ H///AB/wBgAf//8AD/AGAB///wAH8AYAH///AAPwBAAf//8AA/AEAD///wAD8AQAP///AAPwBAB///+A
+ A/AEAP///8AD4AAA////4AcAAAH////wDgAAAf/////8AAAH//////gAAAf/////4AAAAf/////gAAAA
+ /f//+AAAAAAAD//AAAAAAAAH/4AAAAAAAAf/gAAAAAAAB/+AAAAAAAAH/4AAAAAAAAf/gAAAAAAAB/+A
+ AAAAAAAP/4AAAAAAAB//wAAAAABAf/4HwAAAAYAf8APAAAADgA/gA+AAAAMAA8AD8AAABwADgAP8AAAf
+ AAOAA/4AAB8AA4ADAAAAAQADgAIAcA4AgAOABgBwDgBAA4AMAGAMADADwDwAYAwAOAfg+ABgBAAeH//4
+ AEAEAB////gAwAYAH///+ADABgAf///4AcAGAB////gBwAcAH///+APAB4A////8B+AHwH//////4A//
+ ///////gD/////////Af//////////////8=
+
+
+
\ No newline at end of file
diff --git a/BrowseForm.Designer.cs b/BrowseForm.Designer.cs
new file mode 100644
index 0000000..9c9c3f7
--- /dev/null
+++ b/BrowseForm.Designer.cs
@@ -0,0 +1,846 @@
+namespace CodeWalker
+{
+ partial class BrowseForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(BrowseForm));
+ this.label1 = new System.Windows.Forms.Label();
+ this.FolderBrowseButton = new System.Windows.Forms.Button();
+ this.FolderTextBox = new System.Windows.Forms.TextBox();
+ this.ScanButton = new System.Windows.Forms.Button();
+ this.FolderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
+ this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+ this.tabControl1 = new System.Windows.Forms.TabControl();
+ this.tabPage1 = new System.Windows.Forms.TabPage();
+ this.FlattenStructureCheckBox = new System.Windows.Forms.CheckBox();
+ this.FindButton = new System.Windows.Forms.Button();
+ this.MainTreeView = new System.Windows.Forms.TreeView();
+ this.label2 = new System.Windows.Forms.Label();
+ this.FindTextBox = new System.Windows.Forms.TextBox();
+ this.tabPage2 = new System.Windows.Forms.TabPage();
+ this.SearchSaveResultsButton = new System.Windows.Forms.Button();
+ this.SearchIgnoreTextBox = new System.Windows.Forms.TextBox();
+ this.SearchIgnoreCheckBox = new System.Windows.Forms.CheckBox();
+ this.SearchBothDirectionsCheckBox = new System.Windows.Forms.CheckBox();
+ this.SearchCaseSensitiveCheckBox = new System.Windows.Forms.CheckBox();
+ this.SearchHexRadioButton = new System.Windows.Forms.RadioButton();
+ this.SearchTextRadioButton = new System.Windows.Forms.RadioButton();
+ this.SearchResultsListView = new System.Windows.Forms.ListView();
+ this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.SearchAbortButton = new System.Windows.Forms.Button();
+ this.SearchButton = new System.Windows.Forms.Button();
+ this.label3 = new System.Windows.Forms.Label();
+ this.SearchTextBox = new System.Windows.Forms.TextBox();
+ this.ExportCompressCheckBox = new System.Windows.Forms.CheckBox();
+ this.ExportButton = new System.Windows.Forms.Button();
+ this.FileInfoLabel = new System.Windows.Forms.Label();
+ this.SelectionTabControl = new System.Windows.Forms.TabControl();
+ this.tabPage3 = new System.Windows.Forms.TabPage();
+ this.ShowLargeFileContentsCheckBox = new System.Windows.Forms.CheckBox();
+ this.DataHexLineCombo = new System.Windows.Forms.ComboBox();
+ this.DataTextRadio = new System.Windows.Forms.RadioButton();
+ this.DataHexRadio = new System.Windows.Forms.RadioButton();
+ this.DataTextBox = new System.Windows.Forms.TextBox();
+ this.tabPage4 = new System.Windows.Forms.TabPage();
+ this.DetailsPropertyGrid = new CodeWalker.WinForms.PropertyGridFix();
+ this.TexturesTabPage = new System.Windows.Forms.TabPage();
+ this.splitContainer2 = new System.Windows.Forms.SplitContainer();
+ this.SelTexturesListView = new System.Windows.Forms.ListView();
+ this.columnHeader5 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.SelTextureMipLabel = new System.Windows.Forms.Label();
+ this.label5 = new System.Windows.Forms.Label();
+ this.SelTextureDimensionsLabel = new System.Windows.Forms.Label();
+ this.SelTextureMipTrackBar = new System.Windows.Forms.TrackBar();
+ this.label4 = new System.Windows.Forms.Label();
+ this.SelTextureNameTextBox = new System.Windows.Forms.TextBox();
+ this.SelTexturePictureBox = new System.Windows.Forms.PictureBox();
+ this.MainStatusStrip = new System.Windows.Forms.StatusStrip();
+ this.StatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
+ this.TestAllButton = new System.Windows.Forms.Button();
+ this.AbortButton = new System.Windows.Forms.Button();
+ this.SaveFileDialog = new System.Windows.Forms.SaveFileDialog();
+ ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+ this.splitContainer1.Panel1.SuspendLayout();
+ this.splitContainer1.Panel2.SuspendLayout();
+ this.splitContainer1.SuspendLayout();
+ this.tabControl1.SuspendLayout();
+ this.tabPage1.SuspendLayout();
+ this.tabPage2.SuspendLayout();
+ this.SelectionTabControl.SuspendLayout();
+ this.tabPage3.SuspendLayout();
+ this.tabPage4.SuspendLayout();
+ this.TexturesTabPage.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit();
+ this.splitContainer2.Panel1.SuspendLayout();
+ this.splitContainer2.Panel2.SuspendLayout();
+ this.splitContainer2.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.SelTextureMipTrackBar)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.SelTexturePictureBox)).BeginInit();
+ this.MainStatusStrip.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(12, 9);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(68, 13);
+ this.label1.TabIndex = 47;
+ this.label1.Text = "GTAV folder:";
+ //
+ // FolderBrowseButton
+ //
+ this.FolderBrowseButton.Location = new System.Drawing.Point(347, 4);
+ this.FolderBrowseButton.Name = "FolderBrowseButton";
+ this.FolderBrowseButton.Size = new System.Drawing.Size(27, 23);
+ this.FolderBrowseButton.TabIndex = 2;
+ this.FolderBrowseButton.Text = "...";
+ this.FolderBrowseButton.UseVisualStyleBackColor = true;
+ this.FolderBrowseButton.Click += new System.EventHandler(this.FolderBrowseButton_Click);
+ //
+ // FolderTextBox
+ //
+ this.FolderTextBox.Location = new System.Drawing.Point(90, 6);
+ this.FolderTextBox.Name = "FolderTextBox";
+ this.FolderTextBox.ReadOnly = true;
+ this.FolderTextBox.Size = new System.Drawing.Size(251, 20);
+ this.FolderTextBox.TabIndex = 1;
+ //
+ // ScanButton
+ //
+ this.ScanButton.Location = new System.Drawing.Point(380, 4);
+ this.ScanButton.Name = "ScanButton";
+ this.ScanButton.Size = new System.Drawing.Size(75, 23);
+ this.ScanButton.TabIndex = 0;
+ this.ScanButton.Text = "Scan";
+ this.ScanButton.UseVisualStyleBackColor = true;
+ this.ScanButton.Click += new System.EventHandler(this.ScanButton_Click);
+ //
+ // splitContainer1
+ //
+ this.splitContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.splitContainer1.Location = new System.Drawing.Point(0, 32);
+ this.splitContainer1.Name = "splitContainer1";
+ //
+ // splitContainer1.Panel1
+ //
+ this.splitContainer1.Panel1.Controls.Add(this.tabControl1);
+ //
+ // splitContainer1.Panel2
+ //
+ this.splitContainer1.Panel2.Controls.Add(this.ExportCompressCheckBox);
+ this.splitContainer1.Panel2.Controls.Add(this.ExportButton);
+ this.splitContainer1.Panel2.Controls.Add(this.FileInfoLabel);
+ this.splitContainer1.Panel2.Controls.Add(this.SelectionTabControl);
+ this.splitContainer1.Size = new System.Drawing.Size(855, 492);
+ this.splitContainer1.SplitterDistance = 281;
+ this.splitContainer1.TabIndex = 5;
+ //
+ // tabControl1
+ //
+ this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.tabControl1.Controls.Add(this.tabPage1);
+ this.tabControl1.Controls.Add(this.tabPage2);
+ this.tabControl1.Location = new System.Drawing.Point(0, 3);
+ this.tabControl1.Name = "tabControl1";
+ this.tabControl1.SelectedIndex = 0;
+ this.tabControl1.Size = new System.Drawing.Size(278, 489);
+ this.tabControl1.TabIndex = 0;
+ //
+ // tabPage1
+ //
+ this.tabPage1.Controls.Add(this.FlattenStructureCheckBox);
+ this.tabPage1.Controls.Add(this.FindButton);
+ this.tabPage1.Controls.Add(this.MainTreeView);
+ this.tabPage1.Controls.Add(this.label2);
+ this.tabPage1.Controls.Add(this.FindTextBox);
+ this.tabPage1.Location = new System.Drawing.Point(4, 22);
+ this.tabPage1.Name = "tabPage1";
+ this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
+ this.tabPage1.Size = new System.Drawing.Size(270, 463);
+ this.tabPage1.TabIndex = 0;
+ this.tabPage1.Text = "Browse";
+ this.tabPage1.UseVisualStyleBackColor = true;
+ //
+ // FlattenStructureCheckBox
+ //
+ this.FlattenStructureCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.FlattenStructureCheckBox.AutoSize = true;
+ this.FlattenStructureCheckBox.Location = new System.Drawing.Point(3, 443);
+ this.FlattenStructureCheckBox.Name = "FlattenStructureCheckBox";
+ this.FlattenStructureCheckBox.Size = new System.Drawing.Size(128, 17);
+ this.FlattenStructureCheckBox.TabIndex = 50;
+ this.FlattenStructureCheckBox.Text = "Flatten RPF Structure";
+ this.FlattenStructureCheckBox.UseVisualStyleBackColor = true;
+ this.FlattenStructureCheckBox.CheckedChanged += new System.EventHandler(this.FlattenStructureCheckBox_CheckedChanged);
+ //
+ // FindButton
+ //
+ this.FindButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.FindButton.Location = new System.Drawing.Point(240, 4);
+ this.FindButton.Name = "FindButton";
+ this.FindButton.Size = new System.Drawing.Size(27, 22);
+ this.FindButton.TabIndex = 11;
+ this.FindButton.Text = ">";
+ this.FindButton.UseVisualStyleBackColor = true;
+ this.FindButton.Click += new System.EventHandler(this.FindButton_Click);
+ //
+ // MainTreeView
+ //
+ this.MainTreeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.MainTreeView.HideSelection = false;
+ this.MainTreeView.Location = new System.Drawing.Point(0, 32);
+ this.MainTreeView.Name = "MainTreeView";
+ this.MainTreeView.Size = new System.Drawing.Size(267, 408);
+ this.MainTreeView.TabIndex = 12;
+ this.MainTreeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.MainTreeView_AfterSelect);
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(4, 8);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(30, 13);
+ this.label2.TabIndex = 49;
+ this.label2.Text = "Find:";
+ //
+ // FindTextBox
+ //
+ this.FindTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.FindTextBox.Location = new System.Drawing.Point(38, 5);
+ this.FindTextBox.Name = "FindTextBox";
+ this.FindTextBox.Size = new System.Drawing.Size(199, 20);
+ this.FindTextBox.TabIndex = 10;
+ this.FindTextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.FindTextBox_KeyPress);
+ //
+ // tabPage2
+ //
+ this.tabPage2.Controls.Add(this.SearchSaveResultsButton);
+ this.tabPage2.Controls.Add(this.SearchIgnoreTextBox);
+ this.tabPage2.Controls.Add(this.SearchIgnoreCheckBox);
+ this.tabPage2.Controls.Add(this.SearchBothDirectionsCheckBox);
+ this.tabPage2.Controls.Add(this.SearchCaseSensitiveCheckBox);
+ this.tabPage2.Controls.Add(this.SearchHexRadioButton);
+ this.tabPage2.Controls.Add(this.SearchTextRadioButton);
+ this.tabPage2.Controls.Add(this.SearchResultsListView);
+ this.tabPage2.Controls.Add(this.SearchAbortButton);
+ this.tabPage2.Controls.Add(this.SearchButton);
+ this.tabPage2.Controls.Add(this.label3);
+ this.tabPage2.Controls.Add(this.SearchTextBox);
+ this.tabPage2.Location = new System.Drawing.Point(4, 22);
+ this.tabPage2.Name = "tabPage2";
+ this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
+ this.tabPage2.Size = new System.Drawing.Size(270, 463);
+ this.tabPage2.TabIndex = 1;
+ this.tabPage2.Text = "Search";
+ this.tabPage2.UseVisualStyleBackColor = true;
+ //
+ // SearchSaveResultsButton
+ //
+ this.SearchSaveResultsButton.Enabled = false;
+ this.SearchSaveResultsButton.Location = new System.Drawing.Point(178, 95);
+ this.SearchSaveResultsButton.Name = "SearchSaveResultsButton";
+ this.SearchSaveResultsButton.Size = new System.Drawing.Size(86, 22);
+ this.SearchSaveResultsButton.TabIndex = 19;
+ this.SearchSaveResultsButton.Text = "Save results...";
+ this.SearchSaveResultsButton.UseVisualStyleBackColor = true;
+ this.SearchSaveResultsButton.Click += new System.EventHandler(this.SearchSaveResultsButton_Click);
+ //
+ // SearchIgnoreTextBox
+ //
+ this.SearchIgnoreTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.SearchIgnoreTextBox.Location = new System.Drawing.Point(65, 70);
+ this.SearchIgnoreTextBox.Name = "SearchIgnoreTextBox";
+ this.SearchIgnoreTextBox.Size = new System.Drawing.Size(202, 20);
+ this.SearchIgnoreTextBox.TabIndex = 16;
+ this.SearchIgnoreTextBox.Text = ".ydr, .ydd, .ytd, .yft, .ybn, .ycd, .awc, .bik";
+ //
+ // SearchIgnoreCheckBox
+ //
+ this.SearchIgnoreCheckBox.AutoSize = true;
+ this.SearchIgnoreCheckBox.Checked = true;
+ this.SearchIgnoreCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+ this.SearchIgnoreCheckBox.Location = new System.Drawing.Point(6, 72);
+ this.SearchIgnoreCheckBox.Name = "SearchIgnoreCheckBox";
+ this.SearchIgnoreCheckBox.Size = new System.Drawing.Size(59, 17);
+ this.SearchIgnoreCheckBox.TabIndex = 15;
+ this.SearchIgnoreCheckBox.Text = "Ignore:";
+ this.SearchIgnoreCheckBox.UseVisualStyleBackColor = true;
+ this.SearchIgnoreCheckBox.CheckedChanged += new System.EventHandler(this.SearchIgnoreCheckBox_CheckedChanged);
+ //
+ // SearchBothDirectionsCheckBox
+ //
+ this.SearchBothDirectionsCheckBox.AutoSize = true;
+ this.SearchBothDirectionsCheckBox.Checked = true;
+ this.SearchBothDirectionsCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+ this.SearchBothDirectionsCheckBox.Location = new System.Drawing.Point(106, 50);
+ this.SearchBothDirectionsCheckBox.Name = "SearchBothDirectionsCheckBox";
+ this.SearchBothDirectionsCheckBox.Size = new System.Drawing.Size(96, 17);
+ this.SearchBothDirectionsCheckBox.TabIndex = 14;
+ this.SearchBothDirectionsCheckBox.Text = "Both directions";
+ this.SearchBothDirectionsCheckBox.UseVisualStyleBackColor = true;
+ //
+ // SearchCaseSensitiveCheckBox
+ //
+ this.SearchCaseSensitiveCheckBox.AutoSize = true;
+ this.SearchCaseSensitiveCheckBox.Location = new System.Drawing.Point(6, 50);
+ this.SearchCaseSensitiveCheckBox.Name = "SearchCaseSensitiveCheckBox";
+ this.SearchCaseSensitiveCheckBox.Size = new System.Drawing.Size(94, 17);
+ this.SearchCaseSensitiveCheckBox.TabIndex = 13;
+ this.SearchCaseSensitiveCheckBox.Text = "Case-sensitive";
+ this.SearchCaseSensitiveCheckBox.UseVisualStyleBackColor = true;
+ //
+ // SearchHexRadioButton
+ //
+ this.SearchHexRadioButton.AutoSize = true;
+ this.SearchHexRadioButton.Location = new System.Drawing.Point(155, 7);
+ this.SearchHexRadioButton.Name = "SearchHexRadioButton";
+ this.SearchHexRadioButton.Size = new System.Drawing.Size(44, 17);
+ this.SearchHexRadioButton.TabIndex = 12;
+ this.SearchHexRadioButton.Text = "Hex";
+ this.SearchHexRadioButton.UseVisualStyleBackColor = true;
+ this.SearchHexRadioButton.CheckedChanged += new System.EventHandler(this.SearchHexRadioButton_CheckedChanged);
+ //
+ // SearchTextRadioButton
+ //
+ this.SearchTextRadioButton.AutoSize = true;
+ this.SearchTextRadioButton.Checked = true;
+ this.SearchTextRadioButton.Location = new System.Drawing.Point(103, 7);
+ this.SearchTextRadioButton.Name = "SearchTextRadioButton";
+ this.SearchTextRadioButton.Size = new System.Drawing.Size(46, 17);
+ this.SearchTextRadioButton.TabIndex = 11;
+ this.SearchTextRadioButton.TabStop = true;
+ this.SearchTextRadioButton.Text = "Text";
+ this.SearchTextRadioButton.UseVisualStyleBackColor = true;
+ this.SearchTextRadioButton.CheckedChanged += new System.EventHandler(this.SearchTextRadioButton_CheckedChanged);
+ //
+ // SearchResultsListView
+ //
+ this.SearchResultsListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.SearchResultsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnHeader1,
+ this.columnHeader2});
+ this.SearchResultsListView.FullRowSelect = true;
+ this.SearchResultsListView.HideSelection = false;
+ this.SearchResultsListView.Location = new System.Drawing.Point(0, 123);
+ this.SearchResultsListView.MultiSelect = false;
+ this.SearchResultsListView.Name = "SearchResultsListView";
+ this.SearchResultsListView.Size = new System.Drawing.Size(267, 340);
+ this.SearchResultsListView.TabIndex = 20;
+ this.SearchResultsListView.UseCompatibleStateImageBehavior = false;
+ this.SearchResultsListView.View = System.Windows.Forms.View.Details;
+ this.SearchResultsListView.VirtualMode = true;
+ this.SearchResultsListView.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.SearchResultsListView_RetrieveVirtualItem);
+ this.SearchResultsListView.SelectedIndexChanged += new System.EventHandler(this.SearchResultsListView_SelectedIndexChanged);
+ //
+ // columnHeader1
+ //
+ this.columnHeader1.Text = "File";
+ this.columnHeader1.Width = 176;
+ //
+ // columnHeader2
+ //
+ this.columnHeader2.Text = "Offset";
+ //
+ // SearchAbortButton
+ //
+ this.SearchAbortButton.Location = new System.Drawing.Point(87, 95);
+ this.SearchAbortButton.Name = "SearchAbortButton";
+ this.SearchAbortButton.Size = new System.Drawing.Size(75, 22);
+ this.SearchAbortButton.TabIndex = 18;
+ this.SearchAbortButton.Text = "Abort";
+ this.SearchAbortButton.UseVisualStyleBackColor = true;
+ this.SearchAbortButton.Click += new System.EventHandler(this.SearchAbortButton_Click);
+ //
+ // SearchButton
+ //
+ this.SearchButton.Location = new System.Drawing.Point(6, 95);
+ this.SearchButton.Name = "SearchButton";
+ this.SearchButton.Size = new System.Drawing.Size(75, 22);
+ this.SearchButton.TabIndex = 17;
+ this.SearchButton.Text = "Search";
+ this.SearchButton.UseVisualStyleBackColor = true;
+ this.SearchButton.Click += new System.EventHandler(this.SearchButton_Click);
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(4, 9);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(91, 13);
+ this.label3.TabIndex = 52;
+ this.label3.Text = "Search in files for:";
+ //
+ // SearchTextBox
+ //
+ this.SearchTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.SearchTextBox.Location = new System.Drawing.Point(3, 27);
+ this.SearchTextBox.Name = "SearchTextBox";
+ this.SearchTextBox.Size = new System.Drawing.Size(264, 20);
+ this.SearchTextBox.TabIndex = 10;
+ //
+ // ExportCompressCheckBox
+ //
+ this.ExportCompressCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.ExportCompressCheckBox.AutoSize = true;
+ this.ExportCompressCheckBox.Location = new System.Drawing.Point(414, 5);
+ this.ExportCompressCheckBox.Name = "ExportCompressCheckBox";
+ this.ExportCompressCheckBox.Size = new System.Drawing.Size(72, 17);
+ this.ExportCompressCheckBox.TabIndex = 105;
+ this.ExportCompressCheckBox.Text = "Compress";
+ this.ExportCompressCheckBox.UseVisualStyleBackColor = true;
+ //
+ // ExportButton
+ //
+ this.ExportButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.ExportButton.Location = new System.Drawing.Point(488, 2);
+ this.ExportButton.Name = "ExportButton";
+ this.ExportButton.Size = new System.Drawing.Size(75, 23);
+ this.ExportButton.TabIndex = 104;
+ this.ExportButton.Text = "Export...";
+ this.ExportButton.UseVisualStyleBackColor = true;
+ this.ExportButton.Click += new System.EventHandler(this.ExportButton_Click);
+ //
+ // FileInfoLabel
+ //
+ this.FileInfoLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.FileInfoLabel.AutoEllipsis = true;
+ this.FileInfoLabel.Location = new System.Drawing.Point(7, 6);
+ this.FileInfoLabel.Name = "FileInfoLabel";
+ this.FileInfoLabel.Size = new System.Drawing.Size(401, 16);
+ this.FileInfoLabel.TabIndex = 51;
+ this.FileInfoLabel.Text = "[Nothing selected]";
+ //
+ // SelectionTabControl
+ //
+ this.SelectionTabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.SelectionTabControl.Controls.Add(this.tabPage3);
+ this.SelectionTabControl.Controls.Add(this.tabPage4);
+ this.SelectionTabControl.Controls.Add(this.TexturesTabPage);
+ this.SelectionTabControl.Location = new System.Drawing.Point(3, 30);
+ this.SelectionTabControl.Name = "SelectionTabControl";
+ this.SelectionTabControl.SelectedIndex = 0;
+ this.SelectionTabControl.Size = new System.Drawing.Size(564, 462);
+ this.SelectionTabControl.TabIndex = 0;
+ //
+ // tabPage3
+ //
+ this.tabPage3.Controls.Add(this.ShowLargeFileContentsCheckBox);
+ this.tabPage3.Controls.Add(this.DataHexLineCombo);
+ this.tabPage3.Controls.Add(this.DataTextRadio);
+ this.tabPage3.Controls.Add(this.DataHexRadio);
+ this.tabPage3.Controls.Add(this.DataTextBox);
+ this.tabPage3.Location = new System.Drawing.Point(4, 22);
+ this.tabPage3.Name = "tabPage3";
+ this.tabPage3.Padding = new System.Windows.Forms.Padding(3);
+ this.tabPage3.Size = new System.Drawing.Size(556, 436);
+ this.tabPage3.TabIndex = 0;
+ this.tabPage3.Text = "Data";
+ this.tabPage3.UseVisualStyleBackColor = true;
+ //
+ // ShowLargeFileContentsCheckBox
+ //
+ this.ShowLargeFileContentsCheckBox.AutoSize = true;
+ this.ShowLargeFileContentsCheckBox.Location = new System.Drawing.Point(395, 7);
+ this.ShowLargeFileContentsCheckBox.Name = "ShowLargeFileContentsCheckBox";
+ this.ShowLargeFileContentsCheckBox.Size = new System.Drawing.Size(139, 17);
+ this.ShowLargeFileContentsCheckBox.TabIndex = 104;
+ this.ShowLargeFileContentsCheckBox.Text = "Show large file contents";
+ this.ShowLargeFileContentsCheckBox.UseVisualStyleBackColor = true;
+ this.ShowLargeFileContentsCheckBox.CheckedChanged += new System.EventHandler(this.ShowLargeFileContentsCheckBox_CheckedChanged);
+ //
+ // DataHexLineCombo
+ //
+ this.DataHexLineCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.DataHexLineCombo.FormattingEnabled = true;
+ this.DataHexLineCombo.Items.AddRange(new object[] {
+ "8",
+ "16",
+ "32"});
+ this.DataHexLineCombo.Location = new System.Drawing.Point(56, 5);
+ this.DataHexLineCombo.Name = "DataHexLineCombo";
+ this.DataHexLineCombo.Size = new System.Drawing.Size(49, 21);
+ this.DataHexLineCombo.TabIndex = 101;
+ this.DataHexLineCombo.SelectedIndexChanged += new System.EventHandler(this.DataHexLineCombo_SelectedIndexChanged);
+ //
+ // DataTextRadio
+ //
+ this.DataTextRadio.AutoSize = true;
+ this.DataTextRadio.Location = new System.Drawing.Point(176, 6);
+ this.DataTextRadio.Name = "DataTextRadio";
+ this.DataTextRadio.Size = new System.Drawing.Size(46, 17);
+ this.DataTextRadio.TabIndex = 102;
+ this.DataTextRadio.Text = "Text";
+ this.DataTextRadio.UseVisualStyleBackColor = true;
+ this.DataTextRadio.CheckedChanged += new System.EventHandler(this.DataTextRadio_CheckedChanged);
+ //
+ // DataHexRadio
+ //
+ this.DataHexRadio.AutoSize = true;
+ this.DataHexRadio.Checked = true;
+ this.DataHexRadio.Location = new System.Drawing.Point(6, 6);
+ this.DataHexRadio.Name = "DataHexRadio";
+ this.DataHexRadio.Size = new System.Drawing.Size(44, 17);
+ this.DataHexRadio.TabIndex = 100;
+ this.DataHexRadio.TabStop = true;
+ this.DataHexRadio.Text = "Hex";
+ this.DataHexRadio.UseVisualStyleBackColor = true;
+ this.DataHexRadio.CheckedChanged += new System.EventHandler(this.DataHexRadio_CheckedChanged);
+ //
+ // DataTextBox
+ //
+ this.DataTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.DataTextBox.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.DataTextBox.HideSelection = false;
+ this.DataTextBox.Location = new System.Drawing.Point(6, 29);
+ this.DataTextBox.Multiline = true;
+ this.DataTextBox.Name = "DataTextBox";
+ this.DataTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.DataTextBox.Size = new System.Drawing.Size(544, 401);
+ this.DataTextBox.TabIndex = 103;
+ this.DataTextBox.Text = "[Please select a data file]";
+ this.DataTextBox.WordWrap = false;
+ //
+ // tabPage4
+ //
+ this.tabPage4.Controls.Add(this.DetailsPropertyGrid);
+ this.tabPage4.Location = new System.Drawing.Point(4, 22);
+ this.tabPage4.Name = "tabPage4";
+ this.tabPage4.Padding = new System.Windows.Forms.Padding(3);
+ this.tabPage4.Size = new System.Drawing.Size(556, 436);
+ this.tabPage4.TabIndex = 1;
+ this.tabPage4.Text = "Details";
+ this.tabPage4.UseVisualStyleBackColor = true;
+ //
+ // DetailsPropertyGrid
+ //
+ this.DetailsPropertyGrid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.DetailsPropertyGrid.Location = new System.Drawing.Point(6, 6);
+ this.DetailsPropertyGrid.Name = "DetailsPropertyGrid";
+ this.DetailsPropertyGrid.Size = new System.Drawing.Size(544, 424);
+ this.DetailsPropertyGrid.TabIndex = 104;
+ //
+ // TexturesTabPage
+ //
+ this.TexturesTabPage.Controls.Add(this.splitContainer2);
+ this.TexturesTabPage.Location = new System.Drawing.Point(4, 22);
+ this.TexturesTabPage.Name = "TexturesTabPage";
+ this.TexturesTabPage.Size = new System.Drawing.Size(556, 436);
+ this.TexturesTabPage.TabIndex = 2;
+ this.TexturesTabPage.Text = "Textures";
+ this.TexturesTabPage.UseVisualStyleBackColor = true;
+ //
+ // splitContainer2
+ //
+ this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.splitContainer2.Location = new System.Drawing.Point(0, 0);
+ this.splitContainer2.Name = "splitContainer2";
+ //
+ // splitContainer2.Panel1
+ //
+ this.splitContainer2.Panel1.Controls.Add(this.SelTexturesListView);
+ //
+ // splitContainer2.Panel2
+ //
+ this.splitContainer2.Panel2.Controls.Add(this.SelTextureMipLabel);
+ this.splitContainer2.Panel2.Controls.Add(this.label5);
+ this.splitContainer2.Panel2.Controls.Add(this.SelTextureDimensionsLabel);
+ this.splitContainer2.Panel2.Controls.Add(this.SelTextureMipTrackBar);
+ this.splitContainer2.Panel2.Controls.Add(this.label4);
+ this.splitContainer2.Panel2.Controls.Add(this.SelTextureNameTextBox);
+ this.splitContainer2.Panel2.Controls.Add(this.SelTexturePictureBox);
+ this.splitContainer2.Size = new System.Drawing.Size(556, 436);
+ this.splitContainer2.SplitterDistance = 177;
+ this.splitContainer2.TabIndex = 0;
+ //
+ // SelTexturesListView
+ //
+ this.SelTexturesListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.SelTexturesListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnHeader5});
+ this.SelTexturesListView.FullRowSelect = true;
+ this.SelTexturesListView.HideSelection = false;
+ this.SelTexturesListView.Location = new System.Drawing.Point(3, 3);
+ this.SelTexturesListView.MultiSelect = false;
+ this.SelTexturesListView.Name = "SelTexturesListView";
+ this.SelTexturesListView.Size = new System.Drawing.Size(171, 430);
+ this.SelTexturesListView.TabIndex = 21;
+ this.SelTexturesListView.UseCompatibleStateImageBehavior = false;
+ this.SelTexturesListView.View = System.Windows.Forms.View.Details;
+ this.SelTexturesListView.SelectedIndexChanged += new System.EventHandler(this.SelTexturesListView_SelectedIndexChanged);
+ //
+ // columnHeader5
+ //
+ this.columnHeader5.Text = "Texture";
+ this.columnHeader5.Width = 146;
+ //
+ // SelTextureMipLabel
+ //
+ this.SelTextureMipLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.SelTextureMipLabel.AutoSize = true;
+ this.SelTextureMipLabel.Location = new System.Drawing.Point(41, 401);
+ this.SelTextureMipLabel.Name = "SelTextureMipLabel";
+ this.SelTextureMipLabel.Size = new System.Drawing.Size(13, 13);
+ this.SelTextureMipLabel.TabIndex = 44;
+ this.SelTextureMipLabel.Text = "0";
+ //
+ // label5
+ //
+ this.label5.AutoSize = true;
+ this.label5.Location = new System.Drawing.Point(10, 10);
+ this.label5.Name = "label5";
+ this.label5.Size = new System.Drawing.Size(38, 13);
+ this.label5.TabIndex = 43;
+ this.label5.Text = "Name:";
+ //
+ // SelTextureDimensionsLabel
+ //
+ this.SelTextureDimensionsLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.SelTextureDimensionsLabel.AutoSize = true;
+ this.SelTextureDimensionsLabel.Location = new System.Drawing.Point(301, 401);
+ this.SelTextureDimensionsLabel.Name = "SelTextureDimensionsLabel";
+ this.SelTextureDimensionsLabel.Size = new System.Drawing.Size(10, 13);
+ this.SelTextureDimensionsLabel.TabIndex = 42;
+ this.SelTextureDimensionsLabel.Text = "-";
+ //
+ // SelTextureMipTrackBar
+ //
+ this.SelTextureMipTrackBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.SelTextureMipTrackBar.AutoSize = false;
+ this.SelTextureMipTrackBar.BackColor = System.Drawing.SystemColors.ControlLightLight;
+ this.SelTextureMipTrackBar.LargeChange = 1;
+ this.SelTextureMipTrackBar.Location = new System.Drawing.Point(60, 395);
+ this.SelTextureMipTrackBar.Maximum = 0;
+ this.SelTextureMipTrackBar.Name = "SelTextureMipTrackBar";
+ this.SelTextureMipTrackBar.Size = new System.Drawing.Size(234, 31);
+ this.SelTextureMipTrackBar.TabIndex = 41;
+ this.SelTextureMipTrackBar.Scroll += new System.EventHandler(this.SelTextureMipTrackBar_Scroll);
+ //
+ // label4
+ //
+ this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label4.AutoSize = true;
+ this.label4.Location = new System.Drawing.Point(10, 401);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(27, 13);
+ this.label4.TabIndex = 40;
+ this.label4.Text = "Mip:";
+ //
+ // SelTextureNameTextBox
+ //
+ this.SelTextureNameTextBox.Location = new System.Drawing.Point(60, 7);
+ this.SelTextureNameTextBox.Name = "SelTextureNameTextBox";
+ this.SelTextureNameTextBox.Size = new System.Drawing.Size(192, 20);
+ this.SelTextureNameTextBox.TabIndex = 39;
+ //
+ // SelTexturePictureBox
+ //
+ this.SelTexturePictureBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.SelTexturePictureBox.BackColor = System.Drawing.Color.DarkGray;
+ this.SelTexturePictureBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+ this.SelTexturePictureBox.Location = new System.Drawing.Point(3, 33);
+ this.SelTexturePictureBox.Name = "SelTexturePictureBox";
+ this.SelTexturePictureBox.Size = new System.Drawing.Size(369, 351);
+ this.SelTexturePictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.SelTexturePictureBox.TabIndex = 38;
+ this.SelTexturePictureBox.TabStop = false;
+ //
+ // MainStatusStrip
+ //
+ this.MainStatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.StatusLabel});
+ this.MainStatusStrip.Location = new System.Drawing.Point(0, 527);
+ this.MainStatusStrip.Name = "MainStatusStrip";
+ this.MainStatusStrip.Size = new System.Drawing.Size(855, 22);
+ this.MainStatusStrip.TabIndex = 50;
+ this.MainStatusStrip.Text = "statusStrip1";
+ //
+ // StatusLabel
+ //
+ this.StatusLabel.Name = "StatusLabel";
+ this.StatusLabel.Size = new System.Drawing.Size(840, 17);
+ this.StatusLabel.Spring = true;
+ this.StatusLabel.Text = "Scan the GTAV folder to begin.";
+ this.StatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // TestAllButton
+ //
+ this.TestAllButton.Location = new System.Drawing.Point(537, 4);
+ this.TestAllButton.Name = "TestAllButton";
+ this.TestAllButton.Size = new System.Drawing.Size(75, 23);
+ this.TestAllButton.TabIndex = 3;
+ this.TestAllButton.Text = "Test all files";
+ this.TestAllButton.UseVisualStyleBackColor = true;
+ this.TestAllButton.Click += new System.EventHandler(this.TestAllButton_Click);
+ //
+ // AbortButton
+ //
+ this.AbortButton.Location = new System.Drawing.Point(618, 4);
+ this.AbortButton.Name = "AbortButton";
+ this.AbortButton.Size = new System.Drawing.Size(75, 23);
+ this.AbortButton.TabIndex = 4;
+ this.AbortButton.Text = "Abort";
+ this.AbortButton.UseVisualStyleBackColor = true;
+ this.AbortButton.Click += new System.EventHandler(this.AbortButton_Click);
+ //
+ // SaveFileDialog
+ //
+ this.SaveFileDialog.AddExtension = false;
+ //
+ // BrowseForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(855, 549);
+ this.Controls.Add(this.AbortButton);
+ this.Controls.Add(this.TestAllButton);
+ this.Controls.Add(this.MainStatusStrip);
+ this.Controls.Add(this.splitContainer1);
+ this.Controls.Add(this.ScanButton);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.FolderBrowseButton);
+ this.Controls.Add(this.FolderTextBox);
+ this.DoubleBuffered = true;
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.Name = "BrowseForm";
+ this.Text = "RPF Browser - CodeWalker by dexyfex";
+ this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.BrowseForm_FormClosed);
+ this.Load += new System.EventHandler(this.BrowseForm_Load);
+ this.splitContainer1.Panel1.ResumeLayout(false);
+ this.splitContainer1.Panel2.ResumeLayout(false);
+ this.splitContainer1.Panel2.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+ this.splitContainer1.ResumeLayout(false);
+ this.tabControl1.ResumeLayout(false);
+ this.tabPage1.ResumeLayout(false);
+ this.tabPage1.PerformLayout();
+ this.tabPage2.ResumeLayout(false);
+ this.tabPage2.PerformLayout();
+ this.SelectionTabControl.ResumeLayout(false);
+ this.tabPage3.ResumeLayout(false);
+ this.tabPage3.PerformLayout();
+ this.tabPage4.ResumeLayout(false);
+ this.TexturesTabPage.ResumeLayout(false);
+ this.splitContainer2.Panel1.ResumeLayout(false);
+ this.splitContainer2.Panel2.ResumeLayout(false);
+ this.splitContainer2.Panel2.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit();
+ this.splitContainer2.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.SelTextureMipTrackBar)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.SelTexturePictureBox)).EndInit();
+ this.MainStatusStrip.ResumeLayout(false);
+ this.MainStatusStrip.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Button FolderBrowseButton;
+ private System.Windows.Forms.TextBox FolderTextBox;
+ private System.Windows.Forms.Button ScanButton;
+ private System.Windows.Forms.FolderBrowserDialog FolderBrowserDialog;
+ private System.Windows.Forms.SplitContainer splitContainer1;
+ private System.Windows.Forms.TreeView MainTreeView;
+ private System.Windows.Forms.StatusStrip MainStatusStrip;
+ private System.Windows.Forms.ToolStripStatusLabel StatusLabel;
+ private System.Windows.Forms.TabControl tabControl1;
+ private System.Windows.Forms.TabPage tabPage1;
+ private System.Windows.Forms.TabPage tabPage2;
+ private System.Windows.Forms.TabControl SelectionTabControl;
+ private System.Windows.Forms.TabPage tabPage3;
+ private System.Windows.Forms.TabPage tabPage4;
+ private System.Windows.Forms.RadioButton DataTextRadio;
+ private System.Windows.Forms.RadioButton DataHexRadio;
+ private System.Windows.Forms.TextBox DataTextBox;
+ private System.Windows.Forms.ComboBox DataHexLineCombo;
+ private System.Windows.Forms.Label FileInfoLabel;
+ private System.Windows.Forms.Button TestAllButton;
+ private System.Windows.Forms.Button AbortButton;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.TextBox FindTextBox;
+ private System.Windows.Forms.Button FindButton;
+ private System.Windows.Forms.Button ExportButton;
+ private System.Windows.Forms.SaveFileDialog SaveFileDialog;
+ private CodeWalker.WinForms.PropertyGridFix DetailsPropertyGrid;
+ private System.Windows.Forms.Button SearchAbortButton;
+ private System.Windows.Forms.Button SearchButton;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.TextBox SearchTextBox;
+ private System.Windows.Forms.RadioButton SearchHexRadioButton;
+ private System.Windows.Forms.RadioButton SearchTextRadioButton;
+ private System.Windows.Forms.ListView SearchResultsListView;
+ private System.Windows.Forms.ColumnHeader columnHeader1;
+ private System.Windows.Forms.ColumnHeader columnHeader2;
+ private System.Windows.Forms.CheckBox SearchBothDirectionsCheckBox;
+ private System.Windows.Forms.CheckBox SearchCaseSensitiveCheckBox;
+ private System.Windows.Forms.TextBox SearchIgnoreTextBox;
+ private System.Windows.Forms.CheckBox SearchIgnoreCheckBox;
+ private System.Windows.Forms.TabPage TexturesTabPage;
+ private System.Windows.Forms.SplitContainer splitContainer2;
+ private System.Windows.Forms.Label label5;
+ private System.Windows.Forms.Label SelTextureDimensionsLabel;
+ private System.Windows.Forms.TrackBar SelTextureMipTrackBar;
+ private System.Windows.Forms.Label label4;
+ private System.Windows.Forms.TextBox SelTextureNameTextBox;
+ private System.Windows.Forms.PictureBox SelTexturePictureBox;
+ private System.Windows.Forms.ListView SelTexturesListView;
+ private System.Windows.Forms.ColumnHeader columnHeader5;
+ private System.Windows.Forms.Label SelTextureMipLabel;
+ private System.Windows.Forms.CheckBox ShowLargeFileContentsCheckBox;
+ private System.Windows.Forms.CheckBox ExportCompressCheckBox;
+ private System.Windows.Forms.CheckBox FlattenStructureCheckBox;
+ private System.Windows.Forms.Button SearchSaveResultsButton;
+ }
+}
\ No newline at end of file
diff --git a/BrowseForm.cs b/BrowseForm.cs
new file mode 100644
index 0000000..6bc80de
--- /dev/null
+++ b/BrowseForm.cs
@@ -0,0 +1,1288 @@
+using CodeWalker.GameFiles;
+using CodeWalker.Properties;
+using CodeWalker.Utils;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace CodeWalker
+{
+ public partial class BrowseForm : Form
+ {
+ private volatile bool KeysLoaded = false;
+ private volatile bool InProgress = false;
+ private volatile bool AbortOperation = false;
+
+ private int TotalFileCount = 0;
+ private List ScannedFiles = new List();
+ private List RootFiles = new List();
+
+ private List SearchResults = new List();
+ private RpfEntry SelectedEntry = null;
+ private int SelectedOffset = -1;
+ private int SelectedLength = 0;
+
+ private bool TextureTabPageVisible = false;
+
+ private bool FlatStructure = false;
+
+ public BrowseForm()
+ {
+ InitializeComponent();
+ }
+
+ private void BrowseForm_Load(object sender, EventArgs e)
+ {
+ var info = DetailsPropertyGrid.GetType().GetProperty("Controls");
+ var collection = info.GetValue(DetailsPropertyGrid, null) as Control.ControlCollection;
+ foreach (var control in collection)
+ {
+ var ctyp = control.GetType();
+ if (ctyp.Name == "PropertyGridView")
+ {
+ var prop = ctyp.GetField("labelRatio");
+ var val = prop.GetValue(control);
+ prop.SetValue(control, 4.0); //somehow this sets the width of the property grid's label column...
+ }
+ }
+
+ FolderTextBox.Text = GTAFolder.CurrentGTAFolder;
+ DataHexLineCombo.Text = "16";
+
+ DataTextBox.SetTabStopWidth(3);
+
+ HideTexturesTab();
+
+ try
+ {
+ GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key);
+ KeysLoaded = true;
+ UpdateStatus("Ready to scan...");
+ }
+ catch
+ {
+ UpdateStatus("Keys not loaded! This should not happen.");
+ }
+ }
+
+ private void BrowseForm_FormClosed(object sender, FormClosedEventArgs e)
+ {
+ if (!TextureTabPageVisible)
+ {
+ TexturesTabPage.Dispose();
+ }
+ }
+
+ private void FolderBrowseButton_Click(object sender, EventArgs e)
+ {
+ GTAFolder.UpdateGTAFolder(false);
+ FolderTextBox.Text = GTAFolder.CurrentGTAFolder;
+ }
+
+ private void ScanButton_Click(object sender, EventArgs e)
+ {
+ if (InProgress) return;
+
+ if (!KeysLoaded) //this shouldn't ever really happen anymore
+ {
+ MessageBox.Show("Please scan a GTA 5 exe dump for keys first, or include key files in this app's folder!");
+ return;
+ }
+ if (!Directory.Exists(FolderTextBox.Text))
+ {
+ MessageBox.Show("Folder doesn't exist: " + FolderTextBox.Text);
+ return;
+ }
+
+ InProgress = true;
+ AbortOperation = false;
+ ScannedFiles.Clear();
+ RootFiles.Clear();
+
+ MainTreeView.Nodes.Clear();
+
+ string searchpath = FolderTextBox.Text;
+ string replpath = searchpath + "\\";
+
+ Task.Run(() =>
+ {
+
+ UpdateStatus("Starting scan...");
+
+ string[] allfiles = Directory.GetFiles(searchpath, "*.rpf", SearchOption.AllDirectories);
+
+ uint totrpfs = 0;
+ uint totfiles = 0;
+ uint totfolders = 0;
+ uint totresfiles = 0;
+ uint totbinfiles = 0;
+
+ foreach (string rpfpath in allfiles)
+ {
+ if (AbortOperation)
+ {
+ UpdateStatus("Scan aborted!");
+ InProgress = false;
+ return;
+ }
+
+ RpfFile rf = new RpfFile(rpfpath, rpfpath.Replace(replpath, ""));
+
+ UpdateStatus("Scanning " + rf.Name + "...");
+
+ rf.ScanStructure(UpdateStatus, UpdateStatus);
+
+ totrpfs += rf.GrandTotalRpfCount;
+ totfiles += rf.GrandTotalFileCount;
+ totfolders += rf.GrandTotalFolderCount;
+ totresfiles += rf.GrandTotalResourceCount;
+ totbinfiles += rf.GrandTotalBinaryFileCount;
+
+ AddScannedFile(rf, null, true);
+
+ RootFiles.Add(rf);
+ }
+
+ UpdateStatus(string.Format("Scan complete. {0} RPF files, {1} total files, {2} total folders, {3} resources, {4} binary files.", totrpfs, totfiles, totfolders, totresfiles, totbinfiles));
+ InProgress = false;
+ TotalFileCount = (int)totfiles;
+ });
+
+ }
+
+
+
+ private void UpdateStatus(string text)
+ {
+ try
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(() => { UpdateStatus(text); }));
+ }
+ else
+ {
+ StatusLabel.Text = text;
+ }
+ }
+ catch { }
+ }
+
+ private void ClearFiles()
+ {
+ MainTreeView.Nodes.Clear();
+ }
+ private void AddScannedFile(RpfFile file, TreeNode node, bool addToList = false)
+ {
+ try
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(() => { AddScannedFile(file, node, addToList); }));
+ }
+ else
+ {
+
+ var cnode = AddFileNode(file, node);
+
+ if (FlatStructure) cnode = null;
+
+ foreach (RpfFile cfile in file.Children)
+ {
+ AddScannedFile(cfile, cnode, addToList);
+ }
+ if (addToList)
+ {
+ ScannedFiles.Add(file);
+ }
+ }
+ }
+ catch { }
+ }
+ private TreeNode AddFileNode(RpfFile file, TreeNode n)
+ {
+ var nodes = (n == null) ? MainTreeView.Nodes : n.Nodes;
+ TreeNode node = nodes.Add(file.Path);
+ node.Tag = file;
+
+ foreach (RpfEntry entry in file.AllEntries)
+ {
+ if (entry is RpfFileEntry)
+ {
+ bool show = !entry.NameLower.EndsWith(".rpf"); //rpf entries get their own root node..
+ if (show)
+ {
+ //string text = entry.Path.Substring(file.Path.Length + 1); //includes \ on the end
+ //TreeNode cnode = node.Nodes.Add(text);
+ //cnode.Tag = entry;
+ TreeNode cnode = AddEntryNode(entry, node);
+ }
+ }
+ }
+
+
+ //make sure it's all in jenkindex...
+ JenkIndex.Ensure(file.Name);
+ foreach (RpfEntry entry in file.AllEntries)
+ {
+ if (string.IsNullOrEmpty(entry.Name)) continue;
+ JenkIndex.Ensure(entry.Name);
+ JenkIndex.Ensure(entry.NameLower);
+ int ind = entry.Name.LastIndexOf('.');
+ if (ind > 0)
+ {
+ JenkIndex.Ensure(entry.Name.Substring(0, ind));
+ JenkIndex.Ensure(entry.NameLower.Substring(0, ind));
+ }
+ }
+
+ return node;
+
+
+
+
+ //TreeNode lastNode = null;
+ //string subPathAgg;
+ //subPathAgg = string.Empty;
+ //foreach (string subPath in file.Path.Split('\\'))
+ //{
+ // subPathAgg += subPath + '\\';
+ // TreeNode[] nodes = MainTreeView.Nodes.Find(subPathAgg, true);
+ // if (nodes.Length == 0)
+ // {
+ // if (lastNode == null)
+ // {
+ // lastNode = MainTreeView.Nodes.Add(subPathAgg, subPath);
+ // }
+ // else
+ // {
+ // lastNode = lastNode.Nodes.Add(subPathAgg, subPath);
+ // }
+ // }
+ // else
+ // {
+ // lastNode = nodes[0];
+ // }
+ //}
+ //lastNode.Tag = file;
+
+ }
+ private TreeNode AddEntryNode(RpfEntry entry, TreeNode node)
+ {
+ string text = entry.Path.Substring(entry.File.Path.Length + 1); //includes \ on the end
+ TreeNode cnode = (node != null) ? node.Nodes.Add(text) : MainTreeView.Nodes.Add(text);
+ cnode.Tag = entry;
+ return cnode;
+ }
+
+
+ private void MainTreeView_AfterSelect(object sender, TreeViewEventArgs e)
+ {
+ if (e.Node == null) return;
+ if (!e.Node.IsSelected) return; //only do this for selected node
+ if (MainTreeView.SelectedNode == null) return;
+
+ SelectedEntry = MainTreeView.SelectedNode.Tag as RpfEntry;
+ SelectedOffset = -1;
+ SelectedLength = 0;
+
+ SelectFile();
+ }
+ private void DataTextRadio_CheckedChanged(object sender, EventArgs e)
+ {
+ SelectFile();
+ }
+ private void DataHexLineCombo_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ SelectFile();
+ }
+ private void DataHexRadio_CheckedChanged(object sender, EventArgs e)
+ {
+ SelectFile();
+ }
+
+
+ private void SelectFile()
+ {
+ SelectFile(SelectedEntry, SelectedOffset, SelectedLength);
+ }
+ private void SelectFile(RpfEntry entry, int offset, int length)
+ {
+ SelectedEntry = entry;
+ SelectedOffset = offset;
+ SelectedLength = length;
+
+ RpfFileEntry rfe = entry as RpfFileEntry;
+ if (rfe == null)
+ {
+ RpfDirectoryEntry rde = entry as RpfDirectoryEntry;
+ if (rde != null)
+ {
+ FileInfoLabel.Text = rde.Path + " (Directory)";
+ DataTextBox.Text = "[Please select a data file]";
+ }
+ else
+ {
+ FileInfoLabel.Text = "[Nothing selected]";
+ DataTextBox.Text = "[Please select a data file]";
+ }
+ ShowTextures(null);
+ return;
+ }
+
+
+ Cursor = Cursors.WaitCursor;
+
+ string typestr = "Resource";
+ if (rfe is RpfBinaryFileEntry)
+ {
+ typestr = "Binary";
+ }
+
+ byte[] data = rfe.File.ExtractFile(rfe);
+
+ int datalen = (data != null) ? data.Length : 0;
+ FileInfoLabel.Text = rfe.Path + " (" + typestr + " file) - " + TextUtil.GetBytesReadable(datalen);
+
+
+ if (ShowLargeFileContentsCheckBox.Checked || (datalen < 524287)) //512K
+ {
+ DisplayFileContentsText(rfe, data, length, offset);
+ }
+ else
+ {
+ DataTextBox.Text = "[Filesize >512KB. Select the Show large files option to view its contents]";
+ }
+
+
+
+ bool istexdict = false;
+
+
+ if (rfe.NameLower.EndsWith(".ymap"))
+ {
+ YmapFile ymap = new YmapFile(rfe);
+ ymap.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = ymap;
+ }
+ else if (rfe.NameLower.EndsWith(".ytyp"))
+ {
+ YtypFile ytyp = new YtypFile();
+ ytyp.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = ytyp;
+ }
+ else if (rfe.NameLower.EndsWith(".ymf"))
+ {
+ YmfFile ymf = new YmfFile();
+ ymf.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = ymf;
+ }
+ else if (rfe.NameLower.EndsWith(".ymt"))
+ {
+ YmtFile ymt = new YmtFile();
+ ymt.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = ymt;
+ }
+ else if (rfe.NameLower.EndsWith(".ybn"))
+ {
+ YbnFile ybn = new YbnFile();
+ ybn.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = ybn;
+ }
+ else if (rfe.NameLower.EndsWith(".fxc"))
+ {
+ FxcFile fxc = new FxcFile();
+ fxc.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = fxc;
+ }
+ else if (rfe.NameLower.EndsWith(".yft"))
+ {
+ YftFile yft = new YftFile();
+ yft.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = yft;
+
+ if ((yft.Fragment != null) && (yft.Fragment.Drawable != null) && (yft.Fragment.Drawable.ShaderGroup != null) && (yft.Fragment.Drawable.ShaderGroup.TextureDictionary != null))
+ {
+ ShowTextures(yft.Fragment.Drawable.ShaderGroup.TextureDictionary);
+ istexdict = true;
+ }
+ }
+ else if (rfe.NameLower.EndsWith(".ydr"))
+ {
+ YdrFile ydr = new YdrFile();
+ ydr.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = ydr;
+
+ if ((ydr.Drawable != null) && (ydr.Drawable.ShaderGroup != null) && (ydr.Drawable.ShaderGroup.TextureDictionary != null))
+ {
+ ShowTextures(ydr.Drawable.ShaderGroup.TextureDictionary);
+ istexdict = true;
+ }
+ }
+ else if (rfe.NameLower.EndsWith(".ydd"))
+ {
+ YddFile ydd = new YddFile();
+ ydd.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = ydd;
+ //todo: show embedded texdicts in ydd's? is this possible?
+ }
+ else if (rfe.NameLower.EndsWith(".ytd"))
+ {
+ YtdFile ytd = new YtdFile();
+ ytd.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = ytd;
+ ShowTextures(ytd.TextureDict);
+ istexdict = true;
+ }
+ else if (rfe.NameLower.EndsWith(".ycd"))
+ {
+ YcdFile ycd = new YcdFile();
+ ycd.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = ycd;
+ }
+ else if (rfe.NameLower.EndsWith(".ynd"))
+ {
+ YndFile ynd = new YndFile();
+ ynd.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = ynd;
+ }
+ else if (rfe.NameLower.EndsWith(".ynv"))
+ {
+ YnvFile ynv = new YnvFile();
+ ynv.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = ynv;
+ }
+ else if (rfe.NameLower.EndsWith("_cache_y.dat"))
+ {
+ CacheDatFile cdf = new CacheDatFile();
+ cdf.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = cdf;
+ }
+ else if (rfe.NameLower.EndsWith(".rel"))
+ {
+ RelFile rel = new RelFile(rfe);
+ rel.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = rel;
+ }
+ else if (rfe.NameLower.EndsWith(".gxt2"))
+ {
+ Gxt2File gxt2 = new Gxt2File();
+ gxt2.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = gxt2;
+ }
+ else if (rfe.NameLower.EndsWith(".pso"))
+ {
+ JPsoFile pso = new JPsoFile();
+ pso.Load(data, rfe);
+ DetailsPropertyGrid.SelectedObject = pso;
+ }
+ else
+ {
+ DetailsPropertyGrid.SelectedObject = null;
+
+ }
+
+
+ if (!istexdict)
+ {
+ ShowTextures(null);
+ }
+
+
+ Cursor = Cursors.Default;
+
+
+ }
+
+ private void DisplayFileContentsText(RpfFileEntry rfe, byte[] data, int length, int offset)
+ {
+ if (data == null)
+ {
+ Cursor = Cursors.Default;
+ DataTextBox.Text = "[Error extracting file! " + rfe.File.LastError + "]";
+ return;
+ }
+
+ int selline = -1;
+ int selstartc = -1;
+ int selendc = -1;
+
+ if (DataHexRadio.Checked)
+ {
+ int charsperln = int.Parse(DataHexLineCombo.Text);
+ int lines = (data.Length / charsperln) + (((data.Length % charsperln) > 0) ? 1 : 0);
+ StringBuilder hexb = new StringBuilder();
+ StringBuilder texb = new StringBuilder();
+ StringBuilder finb = new StringBuilder();
+
+ if (offset > 0)
+ {
+ selline = offset / charsperln;
+ }
+ for (int i = 0; i < lines; i++)
+ {
+ int pos = i * charsperln;
+ int poslim = pos + charsperln;
+ hexb.Clear();
+ texb.Clear();
+ hexb.AppendFormat("{0:X4}: ", pos);
+ for (int c = pos; c < poslim; c++)
+ {
+ if (c < data.Length)
+ {
+ byte b = data[c];
+ hexb.AppendFormat("{0:X2} ", b);
+ if (char.IsControl((char)b))
+ {
+ texb.Append(".");
+ }
+ else
+ {
+ texb.Append(Encoding.ASCII.GetString(data, c, 1));
+ }
+ }
+ else
+ {
+ hexb.Append(" ");
+ texb.Append(" ");
+ }
+ }
+
+ if (i == selline) selstartc = finb.Length;
+
+ finb.AppendLine(hexb.ToString() + "| " + texb.ToString());
+
+ if (i == selline) selendc = finb.Length - 1;
+ }
+
+ DataTextBox.Text = finb.ToString();
+ }
+ else
+ {
+
+ string text = Encoding.UTF8.GetString(data);
+
+
+ DataTextBox.Text = text;
+
+ if (offset > 0)
+ {
+ selstartc = offset;
+ selendc = offset + length;
+ }
+ }
+
+ if ((selstartc > 0) && (selendc > 0))
+ {
+ DataTextBox.SelectionStart = selstartc;
+ DataTextBox.SelectionLength = selendc - selstartc;
+ DataTextBox.ScrollToCaret();
+ }
+
+ }
+
+ private void ShowTextures(TextureDictionary td)
+ {
+ SelTexturesListView.Items.Clear();
+ SelTexturePictureBox.Image = null;
+ SelTextureNameTextBox.Text = string.Empty;
+ SelTextureDimensionsLabel.Text = "-";
+ SelTextureMipLabel.Text = "0";
+ SelTextureMipTrackBar.Value = 0;
+ SelTextureMipTrackBar.Maximum = 0;
+
+ if (td == null)
+ {
+ HideTexturesTab();
+ return;
+ }
+ if (!SelectionTabControl.TabPages.Contains(TexturesTabPage))
+ {
+ SelectionTabControl.TabPages.Add(TexturesTabPage);
+ }
+
+
+ if ((td.Textures == null) || (td.Textures.data_items == null)) return;
+ var texs = td.Textures.data_items;
+
+ for (int i = 0; i < texs.Length; i++)
+ {
+ var tex = texs[i];
+ ListViewItem lvi = SelTexturesListView.Items.Add(tex.Name);
+ lvi.Tag = tex;
+ }
+
+ }
+
+ private void HideTexturesTab()
+ {
+ if (SelectionTabControl.TabPages.Contains(TexturesTabPage))
+ {
+ SelectionTabControl.TabPages.Remove(TexturesTabPage);
+ }
+ }
+
+ private void ShowTextureMip(Texture tex, int mip, bool mipchange)
+ {
+ if (tex == null)
+ {
+ SelTexturePictureBox.Image = null;
+ SelTextureNameTextBox.Text = string.Empty;
+ SelTextureDimensionsLabel.Text = "-";
+ SelTextureMipLabel.Text = "0";
+ SelTextureMipTrackBar.Value = 0;
+ SelTextureMipTrackBar.Maximum = 0;
+ return;
+ }
+
+
+ if (mipchange)
+ {
+ if (mip >= tex.Levels) mip = tex.Levels - 1;
+ }
+ else
+ {
+ SelTextureMipTrackBar.Maximum = tex.Levels - 1;
+ }
+
+ SelTextureNameTextBox.Text = tex.Name;
+
+ try
+ {
+ int cmip = Math.Min(Math.Max(mip, 0), tex.Levels - 1);
+ byte[] pixels = DDSIO.GetPixels(tex, cmip);
+ int w = tex.Width >> cmip;
+ int h = tex.Height >> cmip;
+ Bitmap bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb);
+
+ if (pixels != null)
+ {
+ var BoundsRect = new System.Drawing.Rectangle(0, 0, w, h);
+ BitmapData bmpData = bmp.LockBits(BoundsRect, ImageLockMode.WriteOnly, bmp.PixelFormat);
+ IntPtr ptr = bmpData.Scan0;
+ int bytes = bmpData.Stride * bmp.Height;
+ Marshal.Copy(pixels, 0, ptr, bytes);
+ bmp.UnlockBits(bmpData);
+ }
+
+ SelTexturePictureBox.Image = bmp;
+ SelTextureDimensionsLabel.Text = w.ToString() + " x " + h.ToString();
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Error reading texture mip:\n" + ex.ToString());
+ SelTexturePictureBox.Image = null;
+ }
+
+ }
+
+
+
+ private void AbortButton_Click(object sender, EventArgs e)
+ {
+ AbortOperation = true;
+ }
+
+
+ private void TestAllButton_Click(object sender, EventArgs e)
+ {
+ if (InProgress) return;
+ if (ScannedFiles.Count == 0)
+ {
+ MessageBox.Show("Please scan the GTAV folder first.");
+ return;
+ }
+
+ AbortOperation = false;
+ InProgress = true;
+
+ DataTextBox.Text = string.Empty;
+ FileInfoLabel.Text = "[File test results]";
+
+ Task.Run(() =>
+ {
+
+ UpdateStatus("Starting test...");
+
+ StringBuilder sbout = new StringBuilder();
+ int errcount = 0;
+ int curfile = 1;
+ int totrpfs = ScannedFiles.Count;
+ long totbytes = 0;
+
+ foreach (RpfFile file in ScannedFiles)
+ {
+ if (AbortOperation)
+ {
+ UpdateStatus("Test aborted.");
+ InProgress = false;
+ return;
+ }
+
+ UpdateStatus(curfile.ToString() + "/" + totrpfs.ToString() + ": Testing " + file.FilePath + "...");
+
+ string errorstr = file.TestExtractAllFiles();
+
+ if (!string.IsNullOrEmpty(errorstr))
+ {
+ AddTestError(errorstr);
+ sbout.Append(errorstr);
+ errcount++;
+ }
+
+ totbytes += file.ExtractedByteCount;
+ curfile++;
+ }
+
+
+ UpdateStatus("Test complete. " + errcount.ToString() + " problems encountered, " + totbytes.ToString() + " total bytes extracted.");
+ InProgress = false;
+ });
+ }
+ private void AddTestError(string error)
+ {
+ try
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(() => { AddTestError(error); }));
+ }
+ else
+ {
+ DataTextBox.AppendText(error);
+
+ }
+ }
+ catch { }
+ }
+
+
+
+
+
+
+ private void Find()
+ {
+ if (InProgress) return;
+ if (ScannedFiles.Count == 0)
+ {
+ MessageBox.Show("Please scan the GTAV folder first.");
+ return;
+ }
+
+
+ string find = FindTextBox.Text.ToLowerInvariant();
+ Cursor = Cursors.WaitCursor;
+ if (string.IsNullOrEmpty(find))
+ {
+ ClearFiles();
+ foreach (RpfFile file in RootFiles)
+ {
+ AddScannedFile(file, null); //reset the file tree... slow :(
+ }
+ }
+ else
+ {
+ ClearFiles();
+ int count = 0;
+ int max = 500;
+ foreach (RpfFile file in ScannedFiles)
+ {
+ if (file.Name.ToLowerInvariant().Contains(find))
+ {
+ AddFileNode(file, null);
+ count++;
+ }
+ foreach (RpfEntry entry in file.AllEntries)
+ {
+ if (entry.NameLower.Contains(find))
+ {
+ if (entry is RpfDirectoryEntry)
+ {
+ RpfDirectoryEntry direntry = entry as RpfDirectoryEntry;
+
+ TreeNode node = AddEntryNode(entry, null);
+
+ foreach (RpfFileEntry cfentry in direntry.Files)
+ {
+ //if (cfentry.Name.EndsWith(".rpf", StringComparison.InvariantCultureIgnoreCase)) continue;
+ AddEntryNode(cfentry, node);
+ }
+ count++;
+ }
+ else if (entry is RpfBinaryFileEntry)
+ {
+ if (entry.NameLower.EndsWith(".rpf", StringComparison.InvariantCultureIgnoreCase)) continue;
+ AddEntryNode(entry, null);
+ count++;
+ }
+ else if (entry is RpfResourceFileEntry)
+ {
+ AddEntryNode(entry, null);
+ count++;
+ }
+ }
+ if (count >= max)
+ {
+ MessageBox.Show("Search results limited to " + max.ToString() + " entries.");
+ break;
+ }
+ }
+ if (count >= max)
+ {
+ break;
+ }
+ }
+ }
+ Cursor = Cursors.Default;
+ }
+ private void FindTextBox_KeyPress(object sender, KeyPressEventArgs e)
+ {
+ if (e.KeyChar == 13)
+ {
+ Find();
+ e.Handled = true;
+ }
+ }
+ private void FindButton_Click(object sender, EventArgs e)
+ {
+ Find();
+ }
+
+
+
+ private void ExportButton_Click(object sender, EventArgs e)
+ {
+ if (InProgress) return;
+ if (ScannedFiles.Count == 0)
+ {
+ MessageBox.Show("Please scan the GTAV folder first.");
+ return;
+ }
+ TreeNode node = MainTreeView.SelectedNode;
+ if (node == null)
+ {
+ MessageBox.Show("Please select a file to export.");
+ return;
+ }
+
+ RpfFileEntry rfe = node.Tag as RpfFileEntry;
+ if (rfe == null)
+ {
+ MessageBox.Show("Please select a file to export.");
+ return;
+ }
+
+ SaveFileDialog.FileName = rfe.Name;
+ if (SaveFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ string fpath = SaveFileDialog.FileName;
+
+ byte[] data = rfe.File.ExtractFile(rfe);
+
+
+ if (ExportCompressCheckBox.Checked)
+ {
+ data = ResourceBuilder.Compress(data);
+ }
+
+
+ RpfResourceFileEntry rrfe = rfe as RpfResourceFileEntry;
+ if (rrfe != null) //add resource header if this is a resource file.
+ {
+ data = ResourceBuilder.AddResourceHeader(rrfe, data);
+ }
+
+ if (data == null)
+ {
+ MessageBox.Show("Error extracting file! " + rfe.File.LastError);
+ return;
+ }
+
+ try
+ {
+
+ File.WriteAllBytes(fpath, data);
+
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Error saving file! " + ex.ToString());
+ }
+
+ }
+
+
+
+ }
+
+
+
+
+ private class SearchResult
+ {
+ public RpfFileEntry FileEntry { get; set; }
+ public int Offset { get; set; }
+ public int Length { get; set; }
+
+ public SearchResult(RpfFileEntry entry, int offset, int length)
+ {
+ FileEntry = entry;
+ Offset = offset;
+ Length = length;
+ }
+ }
+ private byte LowerCaseByte(byte b)
+ {
+ if ((b >= 65) && (b <= 90)) //upper case alphabet...
+ {
+ b += 32;
+ }
+ return b;
+ }
+
+ private void AddSearchResult(SearchResult result)
+ {
+ try
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(() => { AddSearchResult(result); }));
+ }
+ else
+ {
+ SearchResults.Add(result);
+ SearchResultsListView.VirtualListSize = SearchResults.Count;
+ }
+ }
+ catch { }
+ }
+
+ private void Search()
+ {
+ if (InProgress) return;
+ if (ScannedFiles.Count == 0)
+ {
+ MessageBox.Show("Please scan the GTAV folder first.");
+ return;
+ }
+ if (SearchTextBox.Text.Length == 0)
+ {
+ MessageBox.Show("Please enter a search term.");
+ return;
+ }
+
+ string searchtxt = SearchTextBox.Text;
+ bool hex = SearchHexRadioButton.Checked;
+ bool casesen = SearchCaseSensitiveCheckBox.Checked || hex;
+ bool bothdirs = SearchBothDirectionsCheckBox.Checked;
+ string[] ignoreexts = null;
+ byte[] searchbytes1;
+ byte[] searchbytes2;
+ int bytelen;
+
+ if (!casesen) searchtxt = searchtxt.ToLowerInvariant(); //case sensitive search in lower case.
+
+ if (SearchIgnoreCheckBox.Checked)
+ {
+ ignoreexts = SearchIgnoreTextBox.Text.Split(',');
+ for (int i = 0; i < ignoreexts.Length; i++)
+ {
+ ignoreexts[i] = ignoreexts[i].Trim();
+ }
+ }
+
+ if (hex)
+ {
+ if (searchtxt.Length < 2)
+ {
+ MessageBox.Show("Please enter at least one byte of hex (2 characters).");
+ return;
+ }
+ try
+ {
+ bytelen = searchtxt.Length / 2;
+ searchbytes1 = new byte[bytelen];
+ searchbytes2 = new byte[bytelen];
+ for (int i = 0; i < bytelen; i++)
+ {
+ searchbytes1[i] = Convert.ToByte(searchtxt.Substring(i * 2, 2), 16);
+ searchbytes2[bytelen - i - 1] = searchbytes1[i];
+ }
+ }
+ catch
+ {
+ MessageBox.Show("Please enter a valid hex string.");
+ return;
+ }
+ }
+ else
+ {
+ bytelen = searchtxt.Length;
+ searchbytes1 = new byte[bytelen];
+ searchbytes2 = new byte[bytelen]; //reversed text...
+ for (int i = 0; i < bytelen; i++)
+ {
+ searchbytes1[i] = (byte)searchtxt[i];
+ searchbytes2[bytelen - i - 1] = searchbytes1[i];
+ }
+ }
+
+ SearchTextBox.Enabled = false;
+ SearchHexRadioButton.Enabled = false;
+ SearchTextRadioButton.Enabled = false;
+ SearchCaseSensitiveCheckBox.Enabled = false;
+ SearchBothDirectionsCheckBox.Enabled = false;
+ SearchIgnoreCheckBox.Enabled = false;
+ SearchIgnoreTextBox.Enabled = false;
+ SearchButton.Enabled = false;
+ SearchSaveResultsButton.Enabled = false;
+
+ InProgress = true;
+ AbortOperation = false;
+ SearchResultsListView.VirtualListSize = 0;
+ SearchResults.Clear();
+ int totfiles = TotalFileCount;
+ int curfile = 0;
+ Task.Run(() =>
+ {
+
+ DateTime starttime = DateTime.Now;
+ int resultcount = 0;
+
+ for (int f = 0; f < ScannedFiles.Count; f++)
+ {
+ var rpffile = ScannedFiles[f];
+
+ //UpdateStatus(string.Format("Searching {0}/{1} : {2}", f, ScannedFiles.Count, rpffile.Path));
+
+ foreach (var entry in rpffile.AllEntries)
+ {
+ var duration = DateTime.Now - starttime;
+ if (AbortOperation)
+ {
+ UpdateStatus(duration.ToString(@"hh\:mm\:ss") + " - Search aborted.");
+ InProgress = false;
+ SearchComplete();
+ return;
+ }
+
+ RpfFileEntry fentry = entry as RpfFileEntry;
+ if (fentry == null) continue;
+
+ curfile++;
+
+ if (fentry.NameLower.EndsWith(".rpf"))
+ { continue; }
+
+ if (ignoreexts != null)
+ {
+ bool ignore = false;
+ for (int i = 0; i < ignoreexts.Length; i++)
+ {
+ if (fentry.NameLower.EndsWith(ignoreexts[i]))
+ {
+ ignore = true;
+ break;
+ }
+ }
+ if (ignore)
+ { continue; }
+ }
+
+ UpdateStatus(string.Format("{0} - Searching {1}/{2} : {3}", duration.ToString(@"hh\:mm\:ss"), curfile, totfiles, fentry.Path));
+
+ byte[] filebytes = fentry.File.ExtractFile(fentry);
+ if (filebytes == null) continue;
+
+
+ int hitlen1 = 0;
+ int hitlen2 = 0;
+
+ for (int i = 0; i < filebytes.Length; i++)
+ {
+ byte b = casesen ? filebytes[i] : LowerCaseByte(filebytes[i]);
+ byte b1 = searchbytes1[hitlen1]; //current test byte 1
+ byte b2 = searchbytes2[hitlen2];
+
+ if (b == b1) hitlen1++; else hitlen1 = 0;
+ if (hitlen1 == bytelen)
+ {
+ AddSearchResult(new SearchResult(fentry, (i - bytelen), bytelen));
+ resultcount++;
+ hitlen1 = 0;
+ }
+ if (bothdirs)
+ {
+ if (b == b2) hitlen2++; else hitlen2 = 0;
+ if (hitlen2 == bytelen)
+ {
+ AddSearchResult(new SearchResult(fentry, (i - bytelen), bytelen));
+ resultcount++;
+ hitlen2 = 0;
+ }
+ }
+ }
+ }
+ }
+
+ var totdur = DateTime.Now - starttime;
+ UpdateStatus(totdur.ToString(@"hh\:mm\:ss") + " - Search complete. " + resultcount.ToString() + " results found.");
+ InProgress = false;
+ SearchComplete();
+ });
+ }
+ private void SearchComplete()
+ {
+ try
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(() => { SearchComplete(); }));
+ }
+ else
+ {
+ SearchTextBox.Enabled = true;
+ SearchHexRadioButton.Enabled = true;
+ SearchTextRadioButton.Enabled = true;
+ SearchCaseSensitiveCheckBox.Enabled = SearchTextRadioButton.Checked;
+ SearchBothDirectionsCheckBox.Enabled = true;
+ SearchIgnoreCheckBox.Enabled = true;
+ SearchIgnoreTextBox.Enabled = SearchIgnoreCheckBox.Checked;
+ SearchButton.Enabled = true;
+ SearchSaveResultsButton.Enabled = true;
+ }
+ }
+ catch { }
+ }
+ private void SearchButton_Click(object sender, EventArgs e)
+ {
+ Search();
+ }
+ private void SearchAbortButton_Click(object sender, EventArgs e)
+ {
+ AbortOperation = true;
+ }
+ private void SearchSaveResultsButton_Click(object sender, EventArgs e)
+ {
+ SaveFileDialog.FileName = "SearchResults.txt";
+ if (SaveFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ string fpath = SaveFileDialog.FileName;
+
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine("CodeWalker Search Results for \"" + SearchTextBox.Text + "\"");
+ sb.AppendLine("[File path], [Byte offset]");
+ if (SearchResults != null)
+ {
+ foreach (var r in SearchResults)
+ {
+ sb.AppendLine(r.FileEntry.Path + ", " + r.Offset.ToString());
+ }
+ }
+
+ File.WriteAllText(fpath, sb.ToString());
+
+ }
+ }
+ private void SearchTextRadioButton_CheckedChanged(object sender, EventArgs e)
+ {
+ SearchCaseSensitiveCheckBox.Enabled = SearchTextRadioButton.Checked;
+ }
+ private void SearchHexRadioButton_CheckedChanged(object sender, EventArgs e)
+ {
+
+ }
+ private void SearchResultsListView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
+ {
+ var item = new ListViewItem();
+ if (e.ItemIndex < SearchResults.Count)
+ {
+ SearchResult r = SearchResults[e.ItemIndex];
+ item.Text = r.FileEntry.Name;
+ item.SubItems.Add(r.Offset.ToString());
+ item.Tag = r;
+ }
+ e.Item = item;
+ }
+ private void SearchResultsListView_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (SearchResultsListView.SelectedIndices.Count == 1)
+ {
+ var i = SearchResultsListView.SelectedIndices[0];
+ if ((i >= 0) && (i < SearchResults.Count))
+ {
+ var r = SearchResults[i];
+ SelectFile(r.FileEntry, r.Offset+1, r.Length);
+ }
+ else
+ {
+ SelectFile(null, -1, 0);
+ }
+ }
+ else
+ {
+ SelectFile(null, -1, 0);
+ }
+ }
+ private void SearchIgnoreCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ SearchIgnoreTextBox.Enabled = SearchIgnoreCheckBox.Checked;
+ }
+
+ private void SelTexturesListView_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ Texture tex = null;
+ if (SelTexturesListView.SelectedItems.Count == 1)
+ {
+ tex = SelTexturesListView.SelectedItems[0].Tag as Texture;
+ }
+ ShowTextureMip(tex, 0, false);
+ }
+
+ private void SelTextureMipTrackBar_Scroll(object sender, EventArgs e)
+ {
+ Texture tex = null;
+ if (SelTexturesListView.SelectedItems.Count == 1)
+ {
+ tex = SelTexturesListView.SelectedItems[0].Tag as Texture;
+ }
+ SelTextureMipLabel.Text = SelTextureMipTrackBar.Value.ToString();
+ ShowTextureMip(tex, SelTextureMipTrackBar.Value, true);
+ }
+
+ private void ShowLargeFileContentsCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ SelectFile();
+ }
+
+ private void FlattenStructureCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ FlatStructure = FlattenStructureCheckBox.Checked;
+
+ if (InProgress) return;
+ if (ScannedFiles.Count == 0) return;
+
+ Cursor = Cursors.WaitCursor;
+
+ SearchTextBox.Clear();
+
+ ClearFiles();
+ foreach (RpfFile file in RootFiles)
+ {
+ AddScannedFile(file, null);
+ }
+
+ Cursor = Cursors.Default;
+ }
+ }
+}
diff --git a/BrowseForm.resx b/BrowseForm.resx
new file mode 100644
index 0000000..6337549
--- /dev/null
+++ b/BrowseForm.resx
@@ -0,0 +1,418 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 182, 17
+
+
+ 320, 17
+
+
+
+
+ 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
+ 6P///ysvjWVqtP///////wAAAAAAAAAAAP////D0/0hPpsDG6////////6y02d7k8////3qAx+/z/f//
+ /wAAAAAAAAAAAAAAAAAAAP///////////////8zT8V5ns1Rcrdzh9f///////////wAAAAAAAAAAAAAA
+ AAAAAP////////7+/6ix3nmBxFthtmdwu09WqbC54/v9//r8//j6//39/wAAAAAAAAAAAOjt/H6I0FJc
+ skpSqHF+wRMahFZhs4iT1AsNc1pgrm52v2RsuO/z/gAAAP////////L2/cLJ7rrD64+V4DY+ozU+mYmU
+ 0X2Hy1hfss7V8urv/PP2/v///wAAAP///+Pp+d/k9////////+Pp/4uR3ysymW14xYOM0fD0/P///+Xq
+ +ri/6Pj6/wAAAOrv/j5DnbS75P////////////X4/+/0/ubr+/r7/////////9rh+hgZhKGo2QAAAPDz
+ /eLn+f////j6/2Nqttrg9////+Hn+P3+//3+/1hescLJ6/////L2/eru/AAAAAAAAAAAAP///8rR70tR
+ p/3+//v8/zY6jNPY7////09WqWpwu////wAAAAAAAAAAAAAAAAAAAAAAAPb4/vr7//////v8/5Wd1eHm
+ +P////v8//T3/wAAAAAAAAAAAAAAAP//AAD8PwAA/D8AAPwDAACAAwAAgAMAAIAHAADABwAAwAEAAMAB
+ AAAAAQAAAAEAAAABAAAAAQAAwAcAAOAPAAAoAAAAQAAAAIAAAAABABgAAAAAAAAwAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7//P3//P3//P3/
+ /f7//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/
+ +fv/+fv/+Pr/+fv/+vv//P3//v//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA/f7/+fr/8/b/7PL/5+3/6e/+9Pf/+vv//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAA/P3/9/r/6O7/cXe1UVaet7z17fL/+Pr//f3/AAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+/z/9Pj/4Oj/NzyCUlOd2dz/6O//9Pf//P3/AAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8vb/2+P9X2OmREGLnqPd
+ 4+v/8vb/+/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8fb/
+ 1N35bXK1JSRtbHGz5O7/8fX/+/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA+vv/8PX/3Ob/U1eaDwtXjZLT4+z/8fX/+/z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAA+vv/8fb/2eP+MjR6AAA+c3i34Or/8fX/+/z/AAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vv/8vb/1d/7MS91AAA1UFSS4On/8vb/+/z/AAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ 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
+ /v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/+vz/+fv/+Pr/+Pr/+vv//f3//v//AAAAAAAAAAAAAAAA
+ /v//+fv/9Pf/2+L/SVGtAABsLTaZytL58fX/9/n//f7/AAAAAAAAAAAAAAAA/v7/+/z/9/n/9fj/9vn/
+ 9fj/9vj/+vz//f7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7//f3//f3//f3//v7//v//AAAA
+ AAAAAAAAAAAAAAAAAAAA+/z/9vn/6e//mZ7gTVarr7bp6/H/9fj/+vv//v7/AAAAAAAAAAAAAAAAAAAA
+ /v7//f7/+/z/+/z/+/z//P3//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/f3/+Pr/9fj/6e7/4+n/8fb/9Pf/+Pr//f3/AAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//P3/+fv/+fv/+vv/+Pr/+vv/
+ /P3//v7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7//f7/
+ /f3//P3//f7//v7//v//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////
+ ///////4D/////////AH////////8Af////////wB/////////AH////////8Af////////wB///////
+ //AH////////8Af////////wB/////////AH////////8AfwP//////wB8Af//+Af/AHgB///wA/8AcA
+ H///AB/wBgAf//8AD/AGAB///wAH8AYAH///AAPwBAAf//8AA/AEAD///wAD8AQAP///AAPwBAB///+A
+ A/AEAP///8AD4AAA////4AcAAAH////wDgAAAf/////8AAAH//////gAAAf/////4AAAAf/////gAAAA
+ /f//+AAAAAAAD//AAAAAAAAH/4AAAAAAAAf/gAAAAAAAB/+AAAAAAAAH/4AAAAAAAAf/gAAAAAAAB/+A
+ AAAAAAAP/4AAAAAAAB//wAAAAABAf/4HwAAAAYAf8APAAAADgA/gA+AAAAMAA8AD8AAABwADgAP8AAAf
+ AAOAA/4AAB8AA4ADAAAAAQADgAIAcA4AgAOABgBwDgBAA4AMAGAMADADwDwAYAwAOAfg+ABgBAAeH//4
+ AEAEAB////gAwAYAH///+ADABgAf///4AcAGAB////gBwAcAH///+APAB4A////8B+AHwH//////4A//
+ ///////gD/////////Af//////////////8=
+
+
+
\ No newline at end of file
diff --git a/CodeWalker Explorer.cmd b/CodeWalker Explorer.cmd
new file mode 100644
index 0000000..23f0854
--- /dev/null
+++ b/CodeWalker Explorer.cmd
@@ -0,0 +1 @@
+start codewalker explorer
\ No newline at end of file
diff --git a/CodeWalker Menu.cmd b/CodeWalker Menu.cmd
new file mode 100644
index 0000000..bfd5f75
--- /dev/null
+++ b/CodeWalker Menu.cmd
@@ -0,0 +1 @@
+start codewalker menu
\ No newline at end of file
diff --git a/CodeWalker.Core/CodeWalker.Core.csproj b/CodeWalker.Core/CodeWalker.Core.csproj
new file mode 100644
index 0000000..943760a
--- /dev/null
+++ b/CodeWalker.Core/CodeWalker.Core.csproj
@@ -0,0 +1,145 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {DE50D3A6-B49E-47A0-ABE6-101473A00759}
+ Library
+ Properties
+ CodeWalker.Core
+ CodeWalker.Core
+ v4.5.2
+ 512
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\SharpDX.4.0.1\lib\net45\SharpDX.dll
+
+
+ ..\packages\SharpDX.Mathematics.4.0.1\lib\net45\SharpDX.Mathematics.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs b/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs
new file mode 100644
index 0000000..72af04b
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs
@@ -0,0 +1,792 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TC = System.ComponentModel.TypeConverterAttribute;
+using EXP = System.ComponentModel.ExpandableObjectConverter;
+
+namespace CodeWalker.GameFiles
+{
+ [TC(typeof(EXP))]public class AwcFile : PackedFile
+ {
+ public string Name { get; set; }
+ public RpfFileEntry FileEntry { get; set; }
+ public byte[] Data { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ public uint Magic { get; set; }
+ public ushort Version { get; set; }
+ public ushort Flags { get; set; }
+ public int StreamCount { get; set; }
+ public int InfoOffset { get; set; }
+
+ public bool MultiChannel { get; set; }
+ public byte[] MultiChannelData { get; set; }
+
+ public AwcStreamInfo[] StreamInfos { get; set; }
+ public uint[] AudioIds { get; set; }
+ public AwcAudio[] Audios { get; set; }
+
+ static public void Decrypt_RSXXTEA(byte[] data, uint[] key)
+ {
+ // Rockstar's modified version of XXTEA
+ uint[] blocks = new uint[data.Length / 4];
+ Buffer.BlockCopy(data, 0, blocks, 0, data.Length);
+
+ int block_count = blocks.Length;
+ uint a, b = blocks[0], i;
+
+ i = (uint)(0x9E3779B9 * (6 + 52 / block_count));
+ do
+ {
+ for (int block_index = block_count - 1; block_index >= 0; --block_index)
+ {
+ a = blocks[(block_index > 0 ? block_index : block_count) - 1];
+ b = blocks[block_index] -= (a >> 5 ^ b << 2) + (b >> 3 ^ a << 4) ^ (i ^ b) + (key[block_index & 3 ^ (i >> 2 & 3)] ^ a ^ 0x7B3A207F);
+ }
+ i -= 0x9E3779B9;
+ } while (i != 0);
+
+ Buffer.BlockCopy(blocks, 0, data, 0, data.Length);
+ }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+
+ //adapted from libertyV code
+
+
+ //MemoryStream ms = new MemoryStream(data);
+ Name = entry.Name;
+ FileEntry = entry;
+ Data = data;
+
+ if ((data == null) || (data.Length < 8))
+ {
+ ErrorMessage = "Data null or too short!";
+ return; //nothing to do, not enough data...
+ }
+
+ Endianess endianess = Endianess.LittleEndian;
+
+ Magic = BitConverter.ToUInt32(data, 0);
+ if (Magic != 0x54414441 && Magic != 0x41444154)
+ {
+ if (data.Length % 4 == 0)
+ {
+ Decrypt_RSXXTEA(data, GTA5Keys.PC_AWC_KEY);
+ Magic = BitConverter.ToUInt32(data, 0);
+ } else
+ ErrorMessage = "Corrupted data!";
+ }
+
+ switch (Magic)
+ {
+ default:
+ ErrorMessage = "Unexpected Magic 0x" + Magic.ToString("X");
+ return;
+ case 0x54414441:
+ endianess = Endianess.LittleEndian;
+ break;
+ case 0x41444154:
+ endianess = Endianess.BigEndian;
+ break;
+ }
+
+ using (MemoryStream ms = new MemoryStream(data))
+ {
+ DataReader r = new DataReader(ms, endianess);
+
+ Magic = r.ReadUInt32();
+ Version = r.ReadUInt16();
+ Flags = r.ReadUInt16();
+ StreamCount = r.ReadInt32();
+ InfoOffset = r.ReadInt32();
+
+
+ //notes from libertyV:
+ // first bit - means that there are unknown word for each stream after this header
+ // second bit - I think that it means that not all the tags are in the start of the file, but all the tags of a stream are near the data tag
+ // third bit - Multi channel audio
+
+ if ((Flags >> 8) != 0xFF)
+ {
+ ErrorMessage = "Flags 0 not supported!";
+ return;
+ }
+ if ((Flags & 0xF8) != 0)
+ {
+ //ErrorMessage = "Flags 1 not supported!";
+ //return;
+ }
+
+ MultiChannel = ((Flags & 4) == 4);
+
+
+ var flag0 = ((Flags & 1) == 1);
+ var infoStart = 16 + (flag0 ? (StreamCount * 2) : 0);
+
+ ms.Position = infoStart;
+
+ List infos = new List();
+ Dictionary infoDict = new Dictionary();
+ List audioIds = new List();
+ List audios = new List();
+
+ for (int i = 0; i < StreamCount; i++)
+ {
+ var info = new AwcStreamInfo(r);
+ infos.Add(info);
+ infoDict[info.Id] = info;
+ }
+ for (int i = 0; i < StreamCount; i++)
+ {
+ var info = infos[i];
+ for (int j = 0; j < info.TagCount; j++)
+ {
+ var chunk = new AwcChunkInfo(r);
+ info.Chunks[chunk.Tag] = chunk;
+ }
+ }
+
+ StreamInfos = infos.ToArray();
+
+
+
+ byte hformat = 0xFA;// 250 0x6061D4FA & 0xFF; //JenkHash.GenHash("format");
+ byte hdata = 0x55;// 85 0x5EB5E655 & 0xFF; //JenkHash.GenHash("data");
+ byte hycd = 0x5C;// 92 YCD resource chunk... lip sync anims?
+ byte hunk = 0x36;// 54 unk chunk? small number of bytes (2+)
+
+
+
+ if (MultiChannel)
+ {
+ AwcStreamInfo stream0 = null;
+ if (!infoDict.TryGetValue(0, out stream0))
+ {
+ ErrorMessage = "Couldn't find MultiChannel stream0";
+ return;
+ }
+
+ AwcChunkInfo chunk72 = null;
+ if (!stream0.Chunks.TryGetValue(72, out chunk72))
+ {
+ ErrorMessage = "Couldn't find MultiChannel chunk72";
+ return;
+ }
+
+ ms.Position = chunk72.Offset;
+
+ AwcChannelChunkInfo chanInfo = new AwcChannelChunkInfo(r);
+ if (chanInfo.ChannelCount != StreamCount - 1)
+ {
+ ErrorMessage = "Channel Count did not match Stream Count";
+ return;
+ }
+
+ List chunkItems = new List();
+ for (int i = 0; i < chanInfo.ChannelCount; i++)
+ {
+ var itemInfo = new AwcChannelChunkItemInfo(r);
+ chunkItems.Add(itemInfo);
+ audioIds.Add(infos[i + 1].Id);
+ }
+
+ //AudioStreams.Add(new MultiChannelAudio(new ChunkStream(this.Stream, streamsChunks[0][Tag("data")]), channelsInfoHeader, streamsInfo, header.BigEndian));
+
+ AwcChunkInfo cdata = null;
+ if (!stream0.Chunks.TryGetValue(hdata, out cdata))
+ {
+ ErrorMessage = "Couldn't find Stream 0 data chunk";
+ return;
+ }
+
+ ms.Position = cdata.Offset;
+ var lastPos = cdata.Offset + cdata.Size;
+ //int chunkSize = 0x800;
+ uint bigChunkSize = chanInfo.ChunkSize;
+ var chanCount = chanInfo.ChannelCount;
+
+ MultiChannelData = r.ReadBytes(cdata.Size);
+ ms.Position = cdata.Offset;
+
+ //var d = data;//temporary
+
+ ////this doesn't seem to work :(
+ //while (ms.Position < lastPos)
+ //{
+ // uint totalChunks = 0;
+ // var startPos = ms.Position;
+ // var curPos = startPos;
+ // //byte[] chunkdata = r.ReadBytes(chunkSize);
+ // //ms.Position = startPos;
+ // AwcChannelChunkHeader[] chanHeaders = new AwcChannelChunkHeader[chanCount];
+ // for (int i = 0; i < chanCount; i++)
+ // {
+ // var chanHeader = new AwcChannelChunkHeader(r);
+ // chanHeaders[i] = chanHeader;
+ // totalChunks += chanHeader.ChunkCount;
+ // }
+ // int headerSize = (int)(totalChunks * 4 + chanInfo.ChannelCount * AwcChannelChunkHeader.Size);
+ // headerSize += (((-headerSize) % chunkSize) + chunkSize) % chunkSize; //todo: simplify this!
+ // curPos += headerSize;
+ // AwcChannelChunk[] chanChunks = new AwcChannelChunk[chanCount];
+ // for (int i = 0; i < chanCount; i++)
+ // {
+ // var chanChunk = new AwcChannelChunk(r, chanHeaders[i], chunkItems[i]);
+ // chanChunks[i] = chanChunk;
+ // curPos += chanChunk.TotalDataSize;
+ // }
+ // if (curPos - startPos > chanInfo.ChunkSize)
+ // {
+ // ErrorMessage = "Chunk was bigger than the chunk size";
+ // break;
+ // }
+ // if ((totalChunks == 0) || ((startPos + chanInfo.ChunkSize) > lastPos))
+ // {
+ // ErrorMessage = "Unable to read chunk";
+ // break;
+ // }
+ // var newPos = startPos + bigChunkSize;
+ // if (newPos >= lastPos) break;
+ // ms.Position = newPos;
+ //}
+
+
+
+ }
+ else
+ {
+
+ for (int i = 0; i < StreamCount; i++)
+ {
+ var info = infos[i];
+
+ AwcChunkInfo cformat = null;
+ if (!info.Chunks.TryGetValue(hformat, out cformat))
+ {
+ ErrorMessage = "Couldn't find Stream " + i.ToString() + " format chunk";
+ continue;
+ }
+
+ AwcChunkInfo cdata = null;
+ if (!info.Chunks.TryGetValue(hdata, out cdata))
+ {
+ ErrorMessage = "Couldn't find Stream " + i.ToString() + " data chunk";
+ continue;
+ }
+
+ AwcChunkInfo cycd = null;
+ AwcAudioAnimClipDict oycd = null;
+ if (info.Chunks.TryGetValue(hycd, out cycd))
+ {
+ ms.Position = cycd.Offset;
+ oycd = new AwcAudioAnimClipDict(r, cycd);
+ }
+
+ AwcChunkInfo cunk = null;
+ AwcAudioUnk ounk = null;
+ if (info.Chunks.TryGetValue(hunk, out cunk))
+ {
+ ms.Position = cunk.Offset;
+ ounk = new AwcAudioUnk(r, cunk);
+ }
+
+
+ ms.Position = cformat.Offset;
+ AwcFormatChunk formatChunk = new AwcFormatChunk(r);
+
+ ms.Position = cdata.Offset;
+ AwcAudio audio = new AwcAudio(r, info, formatChunk, cdata);
+
+ audio.ClipDict = oycd;
+ audio.UnkData = ounk;
+
+ audios.Add(audio);
+ audioIds.Add(info.Id);
+ }
+ }
+
+
+ Audios = audios.ToArray();
+ AudioIds = audioIds.ToArray();
+
+
+ }
+
+ }
+
+ }
+
+
+ [TC(typeof(EXP))] public class AwcStreamInfo
+ {
+ public uint RawVal { get; set; }
+ public uint TagCount { get; set; }
+ public uint Id { get; set; }
+
+ public Dictionary Chunks { get; set; } = new Dictionary();
+
+ public AwcStreamInfo(DataReader r)
+ {
+ RawVal = r.ReadUInt32();
+ TagCount = (RawVal >> 29);
+ Id = (RawVal & 0x1FFFFFFF);
+ }
+
+ public override string ToString()
+ {
+ return Id.ToString("X") + ": " + TagCount.ToString() + " tags";
+ }
+ }
+
+ [TC(typeof(EXP))] public class AwcChunkInfo
+ {
+ public ulong RawVal { get; set; }
+ public byte Tag { get; set; }
+ public int Size { get; set; }
+ public int Offset { get; set; }
+
+ public AwcChunkInfo(DataReader r)
+ {
+ RawVal = r.ReadUInt64();
+ Tag = (byte)(RawVal >> 56);
+ Size = (int)((RawVal >> 28) & 0x0FFFFFFF);
+ Offset = (int)(RawVal & 0x0FFFFFFF);
+ }
+
+ public override string ToString()
+ {
+ return Tag.ToString() + ": " + Size.ToString() + ", " + Offset.ToString();
+ }
+ }
+
+ [TC(typeof(EXP))] public class AwcChannelChunkInfo
+ {
+ public uint Unk0 { get; set; }
+ public uint ChunkSize { get; set; }
+ public uint ChannelCount { get; set; }
+
+ public AwcChannelChunkInfo(DataReader r)
+ {
+ Unk0 = r.ReadUInt32();
+ ChunkSize = r.ReadUInt32();
+ ChannelCount = r.ReadUInt32();
+ }
+
+ public override string ToString()
+ {
+ return Unk0.ToString() + ": " + ChunkSize.ToString() + ", " + ChannelCount.ToString() + " channels";
+ }
+ }
+
+ [TC(typeof(EXP))] public class AwcChannelChunkItemInfo
+ {
+ public uint Id { get; set; }
+ public uint Samples { get; set; }
+ public ushort Unk0 { get; set; }
+ public ushort SamplesPerSecond { get; set; }
+ public byte Unk1 { get; set; }
+ public byte RoundSize { get; set; }
+ public ushort Unk2 { get; set; }
+
+ public AwcChannelChunkItemInfo(DataReader r)
+ {
+ Id = r.ReadUInt32();
+ Samples = r.ReadUInt32();
+ Unk0 = r.ReadUInt16();
+ SamplesPerSecond = r.ReadUInt16();
+ Unk1 = r.ReadByte();
+ RoundSize = r.ReadByte();
+ Unk2 = r.ReadUInt16();
+ }
+
+ public override string ToString()
+ {
+ return Id.ToString() + ": " + Samples.ToString() + " samples, " + SamplesPerSecond.ToString() + " samples/sec, size: " + RoundSize.ToString();
+ }
+ }
+
+ [TC(typeof(EXP))] public class AwcFormatChunk
+ {
+ public uint Samples { get; set; }
+ public int LoopPoint { get; set; }
+ public ushort SamplesPerSecond { get; set; }
+ public short Headroom { get; set; }
+ public ushort LoopBegin { get; set; }
+ public ushort LoopEnd { get; set; }
+ public ushort PlayEnd { get; set; }
+ public byte PlayBegin { get; set; }
+
+ public enum CodecFormat {
+ PCM = 0,
+ ADPCM = 4
+ }
+ public CodecFormat Codec { get; set; }
+
+ public AwcFormatChunk(DataReader r)
+ {
+ Samples = r.ReadUInt32();
+ LoopPoint = r.ReadInt32();
+ SamplesPerSecond = r.ReadUInt16();
+ Headroom = r.ReadInt16();
+ LoopBegin = r.ReadUInt16();
+ LoopEnd = r.ReadUInt16();
+ PlayEnd = r.ReadUInt16();
+ PlayBegin = r.ReadByte();
+ Codec = (CodecFormat)r.ReadByte();
+
+ //Apparently sometimes this struct is longer? TODO: fix??
+ //r.ReadUInt16();
+ //r.ReadUInt16();
+ }
+
+
+ public override string ToString()
+ {
+ return Headroom.ToString() + ", " + Codec.ToString() + ": " + Samples.ToString() + " samples, " + SamplesPerSecond.ToString() + " samples/sec";
+ }
+ }
+
+ [TC(typeof(EXP))] public class AwcAudio
+ {
+ public AwcStreamInfo StreamInfo { get; set; }
+ public AwcFormatChunk Format { get; set; }
+ public AwcChunkInfo DataInfo { get; set; }
+ public byte[] Data { get; set; }
+
+ public AwcAudioAnimClipDict ClipDict { get; set; }
+ public AwcAudioUnk UnkData { get; set; }
+
+
+ public short Channels = 1;
+ public short BitsPerSample = 16;
+ public int SamplesPerSecond
+ {
+ get
+ {
+ return Format?.SamplesPerSecond ?? 0;
+ }
+ }
+ public int SampleCount
+ {
+ get
+ {
+ return (int)(Format?.Samples ?? 0);
+ }
+ }
+
+
+ public string Name
+ {
+ get
+ {
+ return "0x" + StreamInfo?.Id.ToString("X").PadLeft(8, '0') ?? "0";
+ }
+ }
+ public string Type
+ {
+ get
+ {
+ if (Format == null) return "Unknown";
+
+ string codec;
+ switch (Format.Codec)
+ {
+ case AwcFormatChunk.CodecFormat.PCM:
+ codec = "PCM";
+ break;
+ case AwcFormatChunk.CodecFormat.ADPCM:
+ codec = "ADPCM";
+ break;
+ default:
+ codec = "Unknown";
+ break;
+ }
+
+ var hz = Format.SamplesPerSecond;
+
+ return codec + ((hz > 0) ? (", " + hz.ToString() + " Hz") : "");
+ }
+ }
+
+ public float Length
+ {
+ get
+ {
+ return Format == null ? 0 : (float)Format.Samples / Format.SamplesPerSecond;
+ }
+ }
+
+ public string LengthStr
+ {
+ get
+ {
+ return TimeSpan.FromSeconds(Length).ToString("m\\:ss");
+ }
+ }
+
+ public AwcAudio(DataReader r, AwcStreamInfo s, AwcFormatChunk f, AwcChunkInfo d)
+ {
+ StreamInfo = s;
+ Format = f;
+ DataInfo = d;
+
+ Data = r.ReadBytes(d.Size);
+ }
+
+ public override string ToString()
+ {
+ var hash = (StreamInfo?.Id.ToString("X") ?? "0").PadLeft(8, '0');
+ return "0x" + hash + ": " + Format?.ToString() ?? "AwcAudio";
+ }
+
+ public byte[] DecodeADPCM(byte[] data, int sampleCount)
+ {
+ byte[] dataPCM = new byte[data.Length * 4];
+ int predictor = 0, step_index = 0, step = 0;
+ int readingOffset = 0, writingOffset = 0, bytesInBlock = 0;
+
+ int[] ima_index_table = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+
+ short[] ima_step_table = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+ };
+
+ int clip(int value, int min, int max)
+ {
+ if (value < min)
+ return min;
+ if (value > max)
+ return max;
+ return value;
+ }
+
+ void parseNibble(byte nibble)
+ {
+ step_index = clip(step_index + ima_index_table[nibble], 0, 88);
+
+ int diff = step >> 3;
+ if ((nibble & 4) > 0) diff += step;
+ if ((nibble & 2) > 0) diff += step >> 1;
+ if ((nibble & 1) > 0) diff += step >> 2;
+ if ((nibble & 8) > 0) predictor -= diff;
+ else predictor += diff;
+
+ step = ima_step_table[step_index];
+
+ int samplePCM = clip(predictor, -32768, 32767);
+ dataPCM[writingOffset] = (byte)(samplePCM & 0xFF);
+ dataPCM[writingOffset + 1] = (byte)((samplePCM >> 8) & 0xFF);
+ writingOffset += 2;
+ }
+
+ while ((readingOffset < data.Length) && (sampleCount > 0))
+ {
+ if (bytesInBlock == 0)
+ {
+ step_index = clip(data[readingOffset], 0, 88);
+ predictor = BitConverter.ToInt16(data, readingOffset + 2);
+ step = ima_step_table[step_index];
+ bytesInBlock = 2044;
+ readingOffset += 4;
+ }
+ else
+ {
+ parseNibble((byte)(data[readingOffset] & 0x0F));
+ parseNibble((byte)((data[readingOffset] >> 4) & 0x0F));
+ bytesInBlock--;
+ sampleCount -= 2;
+ readingOffset++;
+ }
+ }
+
+ return dataPCM;
+ }
+
+ public Stream GetWavStream()
+ {
+ byte[] dataPCM = null;
+ int bitsPerSample = BitsPerSample;
+
+ switch (Format.Codec)
+ {
+ case AwcFormatChunk.CodecFormat.PCM:
+ dataPCM = Data;
+ break;
+ case AwcFormatChunk.CodecFormat.ADPCM:
+ dataPCM = new byte[Data.Length];
+ Buffer.BlockCopy(Data, 0, dataPCM, 0, Data.Length);
+ AwcFile.Decrypt_RSXXTEA(dataPCM, GTA5Keys.PC_AWC_KEY);
+ dataPCM = DecodeADPCM(dataPCM, SampleCount);
+ bitsPerSample = 16;
+ break;
+ }
+
+ int byteRate = SamplesPerSecond * Channels * bitsPerSample / 8;
+ short blockAlign = (short)(Channels * bitsPerSample / 8);
+
+ MemoryStream stream = new MemoryStream();
+ BinaryWriter w = new BinaryWriter(stream);
+ int wavLength = 12 + 24 + 8 + Data.Length;
+
+ // RIFF chunk
+ w.Write("RIFF".ToCharArray());
+ w.Write((int)(wavLength - 8));
+ w.Write("WAVE".ToCharArray());
+
+ // fmt sub-chunk
+ w.Write("fmt ".ToCharArray());
+ w.Write((int)16); // fmt size
+ w.Write((short)1); // 1 = WAVE_FORMAT_PCM
+ w.Write((short)Channels);
+ w.Write((int)SamplesPerSecond);
+ w.Write((int)byteRate);
+ w.Write((short)blockAlign);
+ w.Write((short)bitsPerSample);
+
+ // data sub-chunk
+ w.Write("data".ToCharArray());
+ w.Write((int)dataPCM.Length);
+ w.Write(dataPCM);
+
+ w.Flush();
+ stream.Position = 0;
+ return stream;
+ }
+ }
+
+ [TC(typeof(EXP))] public class AwcAudioAnimClipDict
+ {
+ public byte[] Data { get; set; }
+ public ClipDictionary ClipDict { get; set; }
+
+ public AwcAudioAnimClipDict(DataReader r, AwcChunkInfo info)
+ {
+ Data = r.ReadBytes(info.Size);
+
+ if ((Data == null) || (Data.Length < 16)) return;
+
+ var data = Data;
+
+ RpfResourceFileEntry resentry = new RpfResourceFileEntry();
+ uint rsc7 = BitConverter.ToUInt32(data, 0);
+ int version = BitConverter.ToInt32(data, 4);
+ resentry.SystemFlags = BitConverter.ToUInt32(data, 8);
+ resentry.GraphicsFlags = BitConverter.ToUInt32(data, 12);
+
+ if (rsc7 != 0x37435352)
+ { } //testing..
+ if (version != 46) //46 is Clip Dictionary...
+ { }
+
+ int newlen = data.Length - 16; //trim the header from the data passed to the next step.
+ int arrlen = Math.Max(newlen, resentry.SystemSize + resentry.GraphicsSize);//expand it as necessary for the reader.
+ byte[] newdata = new byte[arrlen];
+ Buffer.BlockCopy(data, 16, newdata, 0, newlen);
+ data = newdata;
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+ ClipDict = rd.ReadBlock();
+
+
+ }
+
+ public override string ToString()
+ {
+ return (ClipDict?.ClipsMapEntries ?? 0).ToString() + " entries";
+ }
+ }
+
+ [TC(typeof(EXP))] public class AwcAudioUnk
+ {
+ public byte[] Data { get; set; }
+
+ public AwcAudioUnk(DataReader r, AwcChunkInfo info)
+ {
+ Data = r.ReadBytes(info.Size);
+ }
+
+ public override string ToString()
+ {
+ if (Data == null) return "";
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < Data.Length; i++)
+ {
+ if (sb.Length > 0) sb.Append(' ');
+ sb.Append(Data[i].ToString());
+ }
+ return sb.ToString();
+ }
+ }
+
+
+ [TC(typeof(EXP))] public class AwcChannelChunkHeader
+ {
+ public static uint Size = 16; //24 for ps3...
+
+ public uint StartChunk { get; set; }
+ public uint ChunkCount { get; set; }
+ public uint SamplesToSkip { get; set; } //mostly 0
+ public uint SamplesPerChunk { get; set; }
+ public uint DataSize { get; set; }
+
+ public AwcChannelChunkHeader(DataReader r)
+ {
+ StartChunk = r.ReadUInt32();
+ ChunkCount = r.ReadUInt32();
+ SamplesToSkip = r.ReadUInt32();
+ SamplesPerChunk = r.ReadUInt32();
+ DataSize = ChunkCount * 0x800;
+
+ //for ps3, two extra ints:
+ //uint unk0 = r.ReadUint32();
+ //DataSize = r.ReadUint32();
+
+
+ }
+
+ }
+
+ [TC(typeof(EXP))] public class AwcChannelChunk
+ {
+ public AwcChannelChunkHeader Header { get; set; }
+ public AwcChannelChunkItemInfo Info { get; set; }
+ public byte[] Data { get; set; }
+
+ public uint TotalDataSize { get; set; }
+
+ public AwcChannelChunk(DataReader r, AwcChannelChunkHeader h, AwcChannelChunkItemInfo i)
+ {
+ Header = h;
+ Info = i;
+
+ TotalDataSize = h.DataSize;
+
+ var rs = i?.RoundSize ?? 0;
+ int ds = (int)h.DataSize;
+ if (rs != 0)
+ {
+ TotalDataSize = (uint)(TotalDataSize + (((-ds) % rs) + rs) % rs);
+ }
+ }
+
+ }
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/CacheDatFile.cs b/CodeWalker.Core/GameFiles/FileTypes/CacheDatFile.cs
new file mode 100644
index 0000000..a42b209
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/CacheDatFile.cs
@@ -0,0 +1,700 @@
+using SharpDX;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ public class CacheDatFile : PackedFile
+ {
+ public RpfFileEntry FileEntry { get; set; }
+
+ public string Version { get; set; }
+ public CacheFileDate[] FileDates { get; set; }
+
+ public Dictionary MapNodeDict { get; set; }
+ public MapDataStoreNode[] RootMapNodes { get; set; }
+ //public Dictionary InteriorProxyDict { get; set; }
+ public Dictionary BoundsStoreDict { get; set; }
+
+ public MapDataStoreNode[] AllMapNodes { get; set; }
+ public CInteriorProxy[] AllCInteriorProxies { get; set; }
+ public BoundsStoreItem[] AllBoundsStoreItems { get; set; }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ FileEntry = entry;
+
+ MemoryStream ms = new MemoryStream(data);
+ BinaryReader br = new BinaryReader(ms);
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; (i < 100) && (i < data.Length); i++)
+ {
+ //read version string.
+ byte b = data[i];
+ if (b == 0) break;
+ sb.Append((char)b);
+ }
+ Version = sb.ToString().Replace("[VERSION]", "").Replace("\r", "").Replace("\n", "");
+ sb.Clear();
+ int lastn = 0;
+ int lspos = 0;
+ uint structcount = 0;
+ uint modlen;
+ bool indates = false;
+ List lines = new List();
+ var dates = new List();
+ var allMapNodes = new List();
+ var allCInteriorProxies = new List();
+ var allBoundsStoreItems = new List();
+ for (int i = 100; i < data.Length; i++)
+ {
+ byte b = data[i];
+ if (b == 0)
+ break;
+ if (b == 0xA)
+ {
+ lastn = i;
+ string line = sb.ToString();
+ lines.Add(line);
+ switch (line)
+ {
+ case "":
+ indates = true;
+ break;
+ case "":
+ indates = false;
+ break;
+ case "":
+ break;
+ case "":
+ break;
+ case "fwMapDataStore":
+ ms.Position = i + 1;
+ modlen = br.ReadUInt32();
+ structcount = modlen / 64;
+ lspos = i + (int)modlen + 5;
+ while (ms.Position();
+ var rootMapNodes = new List();
+ foreach (var mapnode in AllMapNodes)
+ {
+ MapNodeDict[mapnode.Name] = mapnode;
+ if (mapnode.ParentName == 0)
+ {
+ rootMapNodes.Add(mapnode);
+ }
+ if (mapnode.UnkExtra != null)
+ { }//notsure what to do with this
+ }
+ foreach (var mapnode in AllMapNodes)
+ {
+ MapDataStoreNode pnode;
+ if (MapNodeDict.TryGetValue(mapnode.ParentName, out pnode))
+ {
+ pnode.AddChildToList(mapnode);
+ }
+ else if ((mapnode.ParentName != 0))
+ { }
+ }
+ foreach (var mapnode in AllMapNodes)
+ {
+ mapnode.ChildrenListToArray();
+ }
+ RootMapNodes = rootMapNodes.ToArray();
+
+
+
+ BoundsStoreDict = new Dictionary();
+ foreach (BoundsStoreItem item in AllBoundsStoreItems)
+ {
+ BoundsStoreItem mbsi = null;
+ if (BoundsStoreDict.TryGetValue(item.Name, out mbsi))
+ { }
+ BoundsStoreDict[item.Name] = item;
+ }
+
+ //InteriorProxyDict = new Dictionary();
+ foreach (CInteriorProxy prx in AllCInteriorProxies)
+ {
+ //CInteriorProxy mprx = null;
+ //if (InteriorProxyDict.TryGetValue(prx.Name, out mprx))
+ //{ }
+ //InteriorProxyDict[prx.Name] = prx;//can't do this! multiples with same name different pos
+
+
+ MapDataStoreNode mnode = null;
+ if (MapNodeDict.TryGetValue(prx.Parent, out mnode))
+ {
+ mnode.AddInteriorToList(prx);
+ }
+ else
+ { }
+ }
+ foreach (var mapnode in AllMapNodes)
+ {
+ mapnode.InteriorProxyListToArray();
+ }
+
+
+ br.Dispose();
+ ms.Dispose();
+
+ }
+
+
+ public void LoadXml(string xml)
+ {
+ }
+
+ public string GetXml()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine(MetaXmlBase.XmlHeader);
+ sb.AppendLine(string.Format("", Version));
+ sb.AppendLine(" ");
+ if (FileDates != null)
+ {
+ foreach (var date in FileDates)
+ {
+ sb.AppendLine(string.Format(" {0}", date.ToCacheFileString()));
+ }
+ }
+ sb.AppendLine(" ");
+ sb.AppendLine(" ");
+ if (AllMapNodes != null)
+ {
+ foreach (var mapnode in AllMapNodes)
+ {
+ sb.AppendLine(" - ");
+ sb.AppendLine(string.Format(" {0}", mapnode.Name.ToCleanString()));
+ sb.AppendLine(string.Format(" {0}", mapnode.ParentName.ToCleanString()));
+ sb.AppendLine(string.Format(" ", mapnode.ContentFlags.ToString()));
+ sb.AppendLine(string.Format(" ", FloatUtil.GetVector3XmlString(mapnode.streamingExtentsMin)));
+ sb.AppendLine(string.Format(" ", FloatUtil.GetVector3XmlString(mapnode.streamingExtentsMax)));
+ sb.AppendLine(string.Format(" ", FloatUtil.GetVector3XmlString(mapnode.entitiesExtentsMin)));
+ sb.AppendLine(string.Format(" ", FloatUtil.GetVector3XmlString(mapnode.entitiesExtentsMax)));
+ sb.AppendLine(string.Format(" ", mapnode.Unk1, mapnode.Unk2, mapnode.Unk3));
+ sb.AppendLine("
");
+ }
+ }
+ sb.AppendLine(" ");
+ sb.AppendLine(" ");
+ if (AllCInteriorProxies != null)
+ {
+ foreach (var intprox in AllCInteriorProxies)
+ {
+ sb.AppendLine(" - ");
+ sb.AppendLine(string.Format(" {0}", intprox.Name.ToCleanString()));
+ sb.AppendLine(string.Format(" {0}", intprox.Parent.ToCleanString()));
+ sb.AppendLine(string.Format(" ", FloatUtil.GetVector3XmlString(intprox.Position)));
+ sb.AppendLine(string.Format(" ", FloatUtil.GetQuaternionXmlString(intprox.Orientation)));
+ sb.AppendLine(string.Format(" ", FloatUtil.GetVector3XmlString(intprox.BBMin)));
+ sb.AppendLine(string.Format(" ", FloatUtil.GetVector3XmlString(intprox.BBMax)));
+ sb.AppendLine(string.Format(" ", intprox.Unk01, intprox.Unk03));
+ sb.AppendLine(string.Format(" ", intprox.Unk11, intprox.Unk12, intprox.Unk13, intprox.Unk14));
+ sb.AppendLine(string.Format(" ", intprox.Unk15, intprox.Unk16, intprox.Unk17, intprox.Unk18));
+ sb.AppendLine("
");
+ }
+ }
+ sb.AppendLine(" ");
+ sb.AppendLine(" ");
+ if (AllBoundsStoreItems != null)
+ {
+ foreach (var bndstore in AllBoundsStoreItems)
+ {
+ sb.AppendLine(" - ");
+ sb.AppendLine(string.Format(" {0}", bndstore.Name.ToCleanString()));
+ sb.AppendLine(string.Format(" ", FloatUtil.GetVector3XmlString(bndstore.Min)));
+ sb.AppendLine(string.Format(" ", FloatUtil.GetVector3XmlString(bndstore.Max)));
+ sb.AppendLine(string.Format(" ", bndstore.Layer));
+ sb.AppendLine("
");
+ }
+ }
+ sb.AppendLine(" ");
+ sb.AppendLine("");
+ return sb.ToString();
+ }
+
+
+ public override string ToString()
+ {
+ if (FileEntry != null)
+ {
+ return FileEntry.ToString();
+ }
+ return base.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class CacheFileDate
+ {
+ public MetaHash FileName { get; set; } //"resource_surrogate:/%s.rpf"
+ public DateTime TimeStamp { get; set; }
+ public uint FileID { get; set; }
+
+ public CacheFileDate(string line)
+ {
+ string[] parts = line.Split(' ');
+ if (parts.Length == 3)
+ {
+ FileName = new MetaHash(uint.Parse(parts[0]));
+ TimeStamp = DateTime.FromFileTimeUtc(long.Parse(parts[1]));
+ FileID = uint.Parse(parts[2]);
+ }
+ else
+ { } //testing
+ }
+
+ public string ToCacheFileString()
+ {
+ return FileName.Hash.ToString() + " " + TimeStamp.ToFileTimeUtc().ToString() + " " + FileID.ToString();
+ }
+
+ public override string ToString()
+ {
+ return FileName.ToString() + ", " + TimeStamp.ToString() + ", " + FileID.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class BoundsStoreItem
+ {
+ public MetaHash Name { get; set; }
+ public Vector3 Min { get; set; }
+ public Vector3 Max { get; set; }
+ public uint Layer { get; set; }
+
+ public BoundsStoreItem(Bounds b)
+ {
+ Name = 0;
+ Min = b.BoundingBoxMin;
+ Max = b.BoundingBoxMax;
+ Layer = 0;
+ }
+ public BoundsStoreItem(BinaryReader br)
+ {
+ Name = new MetaHash(br.ReadUInt32());
+ Min = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Max = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Layer = br.ReadUInt32();
+ }
+
+ public override string ToString()
+ {
+ return Name.ToString() + ", " +
+ Min.ToString() + ", " +
+ Max.ToString() + ", " +
+ Layer.ToString();
+ }
+
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class CInteriorProxy
+ {
+ public uint Unk01 { get; set; }
+ public uint Unk02 { get; set; }
+ public uint Unk03 { get; set; }
+ public MetaHash Name { get; set; }
+ public MetaHash Parent { get; set; }
+ public Vector3 Position { get; set; }
+ public Quaternion Orientation { get; set; }
+ public Vector3 BBMin { get; set; }
+ public Vector3 BBMax { get; set; }
+ public float Unk11 { get; set; }
+ public uint Unk12 { get; set; }
+ public float Unk13 { get; set; }
+ public uint Unk14 { get; set; }
+ public float Unk15 { get; set; }
+ public uint Unk16 { get; set; }
+ public uint Unk17 { get; set; }
+ public uint Unk18 { get; set; }
+
+ public CInteriorProxy(BinaryReader br)
+ {
+ Unk01 = br.ReadUInt32();
+ Unk02 = br.ReadUInt32();
+ Unk03 = br.ReadUInt32();
+ Name = new MetaHash(br.ReadUInt32());
+ Parent = new MetaHash(br.ReadUInt32());
+ Position = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Orientation = new Quaternion(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ BBMin = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ BBMax = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Unk11 = br.ReadSingle();
+ Unk12 = br.ReadUInt32();
+ Unk13 = br.ReadSingle();
+ Unk14 = br.ReadUInt32();
+ Unk15 = br.ReadSingle();
+ Unk16 = br.ReadUInt32();
+ Unk17 = br.ReadUInt32();//could be float
+ Unk18 = br.ReadUInt32();
+
+
+
+ switch (Unk01)
+ {
+ case 0: //v_cashdepot
+ case 19: //dt1_02_carpark
+ case 20: //dt1_03_carpark
+ case 21: //dt1_05_carpark
+ case 4: //dt1_rd1_tun
+ case 14: //id1_11_tunnel1_int
+ case 24: //v_firedept
+ case 3: //id2_21_a_tun1
+ case 22: //po1_08_warehouseint1
+ case 11: //sc1_rd_inttun1
+ case 10: //sc1_rd_inttun2b_end
+ case 18: //bt1_04_carpark
+ case 16: //v_hanger
+ case 1: //ap1_03_lisapark_subway
+ case 13: //kt1_03_carpark_int
+ case 5: //sm20_tun1
+ case 2: //vbca_tunnel1
+ case 15: //cs1_12_tunnel01_int
+ case 6: //cs1_14brailway1
+ case 9: //cs2_roadsb_tunnel_01
+ case 7: //cs3_03railtunnel_int1
+ case 8: //cs4_rwayb_tunnelint
+ case 12: //ch1_roadsdint_tun1
+ case 100: //hei_int_mph_carrierhang3
+ case 47: //xm_x17dlc_int_base_ent
+ break;
+ default:
+ break;
+ }
+
+ if (Unk02 != 0)
+ { }
+
+ switch (Unk03)
+ {
+ case 6: //v_cashdepot
+ case 2: //dt1_02_carpark
+ case 8: //v_fib01
+ case 4: //v_fib03
+ case 0: //v_fib04
+ case 7: //v_clotheslo
+ case 1: //v_gun
+ case 3: //v_genbank
+ case 11: //v_hospital
+ case 5: //v_shop_247
+ case 32: //v_abattoir
+ case 13: //v_franklins
+ case 15: //v_michael
+ case 18: //v_faceoffice
+ case 29: //v_recycle
+ case 9: //v_stadium
+ case 54: //v_farmhouse
+ case 12: //v_ranch
+ case 26: //hei_gta_milo_bar
+ case 17: //hei_gta_milo_bedrm
+ case 14: //hei_gta_milo_bridge
+ case 48: //apa_mpapa_yacht
+ break;
+ default:
+ break;
+ }
+
+
+ if ((Unk12 == 0) || (Unk12 > 0xFFFFFF))
+ { }
+
+ switch (Unk14)
+ {
+ case 1:
+ case 0:
+ case 580:
+ case 355: //sm_smugdlc_int_01
+ case 579: //xm_x17dlc_int_01
+ break;
+ default:
+ break;
+ }
+ switch (Unk16)
+ {
+ case 1:
+ case 32758: //0x7FF6
+ break;
+ default:
+ break;
+ }
+ switch (Unk17) //could be a float..!
+ {
+ case 9:
+ break;
+ case 0x415CBC04: //13.7959f
+ case 0x7B81AC94:
+ case 0x40FE3224: //7.94362f v_gun
+ case 0x41515774: //13.0839f v_gun
+ case 0x414E7B34: //12.9051f bkr_biker_dlc_int_03
+ case 0x41389C14: //11.5381f imp_impexp_int_01
+ case 0x4177B664: //15.482f gr_grdlc_int_01
+ case 0xCE0404F4: // sm_smugdlc_int_01
+ break;
+ default:
+ //string str = JenkIndex.GetString(Unk17);
+ break;
+ }
+ switch(Unk18)
+ {
+ case 0:
+ case 1:
+ case 32758: //0x7FF6
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ public override string ToString()
+ {
+ return Unk01.ToString() + ", " +
+ Unk02.ToString() + ", " +
+ Unk03.ToString() + ", " +
+ Name.ToString() + ", " +
+ Parent.ToString() + ", " +
+ Position.ToString() + ", " +
+ Orientation.ToString() + ", " +
+ BBMin.ToString() + ", " +
+ BBMax.ToString() + ", " +
+ Unk11.ToString() + ", " +
+ Unk12.ToString() + ", " +
+ Unk13.ToString() + ", " +
+ Unk14.ToString() + ", " +
+ Unk15.ToString() + ", " +
+ Unk16.ToString() + ", " +
+ Unk17.ToString() + ", " +
+ Unk18.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class MapDataStoreNode
+ {
+ public MetaHash Name { get; set; }
+ public MetaHash ParentName { get; set; }
+ public uint ContentFlags { get; set; }
+ public Vector3 streamingExtentsMin { get; set; }
+ public Vector3 streamingExtentsMax { get; set; }
+ public Vector3 entitiesExtentsMin { get; set; }
+ public Vector3 entitiesExtentsMax { get; set; }
+ public byte Unk1 { get; set; }
+ public byte Unk2 { get; set; }
+ public byte Unk3 { get; set; }
+ public byte Unk4 { get; set; }
+
+ public MapDataStoreNodeExtra UnkExtra { get; set; }
+
+ public MapDataStoreNode[] Children { get; set; }
+ private List ChildrenList; //used when building the array
+
+ public CInteriorProxy[] InteriorProxies { get; set; }
+ private List InteriorProxyList;
+
+ public MapDataStoreNode(YmapFile ymap)
+ {
+ Name = ymap._CMapData.name;
+ ParentName = ymap._CMapData.parent;
+ ContentFlags = ymap._CMapData.contentFlags;
+ streamingExtentsMin = ymap._CMapData.streamingExtentsMin;
+ streamingExtentsMax = ymap._CMapData.streamingExtentsMax;
+ entitiesExtentsMin = ymap._CMapData.entitiesExtentsMin;
+ entitiesExtentsMax = ymap._CMapData.entitiesExtentsMax;
+ }
+ public MapDataStoreNode(BinaryReader br)
+ {
+ Name = new MetaHash(br.ReadUInt32());
+ ParentName = new MetaHash(br.ReadUInt32());
+ ContentFlags = br.ReadUInt32();
+ streamingExtentsMin = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ streamingExtentsMax = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ entitiesExtentsMin = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ entitiesExtentsMax = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Unk1 = br.ReadByte(); //HD flag? (critical, long, strm)
+ Unk2 = br.ReadByte(); //lod flag? - primary map files
+ Unk3 = br.ReadByte(); //slod flag?
+ Unk4 = br.ReadByte();
+
+ if (Unk1 != 0)
+ { }
+ if (Unk2 != 0)
+ { }
+ if (Unk3 != 0)
+ { }
+ if (Unk4 != 0)
+ { } //no hits here now..
+
+ if (Unk4 == 0xFE)
+ {
+ //this seems to never be hit anymore...
+ UnkExtra = new MapDataStoreNodeExtra(br);
+ }
+ }
+
+
+ public void AddChildToList(MapDataStoreNode child)
+ {
+ if (ChildrenList == null)
+ {
+ ChildrenList = new List();
+ }
+ ChildrenList.Add(child);
+ }
+ public void ChildrenListToArray()
+ {
+ if (ChildrenList != null)
+ {
+ Children = ChildrenList.ToArray();
+ ChildrenList = null; //plz get this GC
+ }
+ }
+ public void AddInteriorToList(CInteriorProxy iprx)
+ {
+ if (InteriorProxyList == null)
+ {
+ InteriorProxyList = new List();
+ }
+ InteriorProxyList.Add(iprx);
+ }
+ public void InteriorProxyListToArray()
+ {
+ if (InteriorProxyList != null)
+ {
+ InteriorProxies = InteriorProxyList.ToArray();
+ InteriorProxyList = null; //plz get this GC
+ }
+ }
+
+
+ public override string ToString()
+ {
+ return Name.ToString() + ", " +
+ ParentName.ToString() + ", " +
+ ContentFlags.ToString() + ", " +
+ streamingExtentsMin.ToString() + ", " +
+ streamingExtentsMax.ToString() + ", " +
+ entitiesExtentsMin.ToString() + ", " +
+ entitiesExtentsMax.ToString();// + ", " +
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class MapDataStoreNodeExtra
+ {
+ public uint Unk01; //0
+ public byte[] Unk02; //1 - 16 (60 bytes)
+ public uint Unk03;//16
+ public uint Unk04;
+ public uint Unk05;
+ public uint Unk06;
+ public uint Unk07;
+ public uint Unk08;
+ public uint Unk09;
+ public uint Unk10;
+
+ public string Unk02str
+ {
+ get
+ {
+ StringBuilder sb = new StringBuilder();
+ if (Unk02 != null)
+ {
+ for (int i = 0; i < Unk02.Length; i++)
+ {
+ if (Unk02[i] == 0) break;
+ sb.Append((char)Unk02[i]);
+ }
+ }
+ return sb.ToString();
+ }
+ }
+
+ public MapDataStoreNodeExtra(BinaryReader br)
+ {
+ Unk01 = br.ReadUInt32();
+
+ Unk02 = new byte[60];
+ for (int i = 0; i < 60; i++)
+ {
+ Unk02[i] = br.ReadByte();
+ }
+ Unk03 = br.ReadUInt32();
+ Unk04 = br.ReadUInt32();
+ Unk05 = br.ReadUInt32();
+ Unk06 = br.ReadUInt32();
+ Unk07 = br.ReadUInt32();
+ Unk08 = br.ReadUInt32();
+ Unk09 = br.ReadUInt32();
+ Unk10 = br.ReadUInt32();
+
+ }
+
+
+ public override string ToString()
+ {
+ return Unk01.ToString() + ", " + Unk02str;
+ }
+ }
+
+
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/CutFile.cs b/CodeWalker.Core/GameFiles/FileTypes/CutFile.cs
new file mode 100644
index 0000000..003302b
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/CutFile.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ public class CutFile : PackedFile
+ {
+ public RpfFileEntry FileEntry { get; set; }
+ public PsoFile Pso { get; set; }
+
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ //MemoryStream ms = new MemoryStream(data);
+
+ FileEntry = entry;
+
+ MemoryStream ms = new MemoryStream(data);
+
+ if (PsoFile.IsPSO(ms))
+ {
+ Pso = new PsoFile();
+ Pso.Load(ms);
+
+ //PsoTypes.EnsurePsoTypes(Pso);
+
+ var root = PsoTypes.GetRootEntry(Pso);
+ if (root != null)
+ {
+ }
+ return;
+ }
+ else
+ {
+
+ }
+
+
+
+
+ }
+
+ }
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/DlcContentFile.cs b/CodeWalker.Core/GameFiles/FileTypes/DlcContentFile.cs
new file mode 100644
index 0000000..078c89b
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/DlcContentFile.cs
@@ -0,0 +1,431 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace CodeWalker.GameFiles
+{
+ public class DlcContentFile
+ {
+
+ public List dataFiles { get; set; } = new List();
+ public List contentChangeSets { get; set; } = new List();
+
+ public RpfFile DlcFile { get; set; } //used by GameFileCache
+ public Dictionary ExtraMounts { get; set; } = new Dictionary();
+ public Dictionary RpfDataFiles { get; set; } = new Dictionary();
+
+ public DlcExtraTitleUpdateFile ExtraTitleUpdates { get; set; }
+
+ public void Load(XmlDocument doc)
+ {
+
+ var root = doc.DocumentElement;
+
+ dataFiles.Clear();
+ contentChangeSets.Clear();
+
+ foreach (XmlNode node in root.ChildNodes)
+ {
+ switch (node.Name)
+ {
+ case "disabledFiles":
+ foreach (XmlNode disabledFile in node.ChildNodes)
+ { } //nothing to see here..
+ break;
+ case "includedXmlFiles":
+ foreach (XmlNode includedXmlFile in node.ChildNodes)
+ { } //nothing to see here..
+ break;
+ case "includedDataFiles":
+ foreach (XmlNode includedDataFile in node.ChildNodes)
+ { } //nothing to see here..
+ break;
+ case "dataFiles":
+ foreach (XmlNode dataFile in node.ChildNodes)
+ {
+ if (dataFile.NodeType == XmlNodeType.Element)
+ {
+ dataFiles.Add(new DlcContentDataFile(dataFile));
+ }
+ }
+ break;
+ case "contentChangeSets":
+ foreach (XmlNode contentChangeSet in node.ChildNodes)
+ {
+ if (contentChangeSet.NodeType == XmlNodeType.Element)
+ {
+ contentChangeSets.Add(new DlcContentChangeSet(contentChangeSet));
+ }
+ }
+ break;
+ case "patchFiles":
+ foreach (XmlNode patchFile in node.ChildNodes)
+ { } //nothing to see here..
+ break;
+ default:
+ break;
+ }
+
+
+ }
+
+
+ }
+
+ public void LoadDicts(DlcSetupFile setupfile, RpfManager rpfman, GameFileCache gfc)
+ {
+ ExtraMounts.Clear();
+ RpfDataFiles.Clear();
+
+ foreach (var datafile in dataFiles)
+ {
+ string dfn = GameFileCache.GetDlcPlatformPath(datafile.filename).ToLowerInvariant();
+ if (datafile.fileType == "EXTRA_FOLDER_MOUNT_DATA")
+ {
+ string efmdxmlpath = datafile.filename.Replace(setupfile.deviceName + ":", DlcFile.Path).Replace('/', '\\');
+ efmdxmlpath = gfc.GetDlcPatchedPath(efmdxmlpath);
+ XmlDocument efmdxml = rpfman.GetFileXml(efmdxmlpath);
+
+ DlcExtraFolderMountFile efmf = new DlcExtraFolderMountFile();
+ efmf.Load(efmdxml);
+
+ ExtraMounts[dfn] = efmf;
+ }
+ if (datafile.fileType == "EXTRA_TITLE_UPDATE_DATA")
+ {
+ string etudxmlpath = datafile.filename.Replace(setupfile.deviceName + ":", DlcFile.Path).Replace('/', '\\');
+ etudxmlpath = gfc.GetDlcPatchedPath(etudxmlpath);
+ XmlDocument etudxml = rpfman.GetFileXml(etudxmlpath);
+
+ DlcExtraTitleUpdateFile etuf = new DlcExtraTitleUpdateFile();
+ etuf.Load(etudxml);
+
+ ExtraTitleUpdates = etuf;
+ }
+ if (datafile.fileType == "RPF_FILE")
+ {
+ RpfDataFiles[dfn] = datafile;
+ }
+ }
+
+ }
+
+ public override string ToString()
+ {
+ return dataFiles.Count.ToString() + " dataFiles, " + contentChangeSets.Count.ToString() + " contentChangeSets";
+ }
+ }
+
+ public class DlcContentDataFile
+ {
+ public string filename { get; set; }
+ public string fileType { get; set; }
+ public string contents { get; set; }
+ public string installPartition { get; set; }
+ public bool overlay { get; set; }
+ public bool disabled { get; set; }
+ public bool persistent { get; set; }
+ public bool loadCompletely { get; set; }
+ public bool locked { get; set; }
+
+ public DlcContentDataFile(XmlNode node)
+ {
+ Load(node);
+ }
+ public void Load(XmlNode node)
+ {
+ foreach (XmlNode child in node.ChildNodes)
+ {
+ switch (child.Name)
+ {
+ case "filename":
+ filename = child.InnerText;
+ break;
+ case "fileType":
+ fileType = child.InnerText;
+ break;
+ case "contents":
+ contents = child.InnerText;
+ break;
+ case "installPartition":
+ installPartition = child.InnerText;
+ break;
+ case "overlay":
+ overlay = Xml.GetBoolAttribute(child, "value");
+ break;
+ case "disabled":
+ disabled = Xml.GetBoolAttribute(child, "value");
+ break;
+ case "persistent":
+ persistent = Xml.GetBoolAttribute(child, "value");
+ break;
+ case "loadCompletely":
+ loadCompletely = Xml.GetBoolAttribute(child, "value");
+ break;
+ case "locked":
+ locked = Xml.GetBoolAttribute(child, "value");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ return filename + ": " + fileType + ": " + contents + ": " + installPartition +
+ (overlay ? ", overlay" : "") +
+ (disabled ? ", disabled" : "") +
+ (persistent ? ", persistent" : "") +
+ (loadCompletely ? ", loadCompletely" : "") +
+ (locked ? ", locked" : "");
+ }
+ }
+
+ public class DlcContentChangeSet
+ {
+ public string changeSetName { get; set; }
+ public List filesToInvalidate { get; set; }
+ public List filesToDisable { get; set; }
+ public List filesToEnable { get; set; }
+ public List txdToLoad { get; set; }
+ public List txdToUnload { get; set; }
+ public List residentResources { get; set; }
+ public List unregisterResources { get; set; }
+ public List mapChangeSetData { get; set; }
+ public string associatedMap { get; set; }
+ public bool requiresLoadingScreen { get; set; }
+ public string loadingScreenContext { get; set; }
+ public bool useCacheLoader { get; set; }
+ public DlcContentChangeSetExecutionConditions executionConditions { get; set; }
+
+ public DlcContentChangeSet(XmlNode node)
+ {
+ Load(node);
+ }
+ public void Load(XmlNode node)
+ {
+ foreach (XmlNode child in node.ChildNodes)
+ {
+ switch (child.Name)
+ {
+ case "changeSetName":
+ changeSetName = child.InnerText;
+ break;
+ case "filesToInvalidate":
+ filesToInvalidate = GetChildStringArray(child);
+ if (filesToInvalidate != null)
+ { }
+ break;
+ case "filesToDisable":
+ filesToDisable = GetChildStringArray(child);
+ if (filesToDisable != null)
+ { }
+ break;
+ case "filesToEnable":
+ filesToEnable = GetChildStringArray(child);
+ if (filesToEnable != null)
+ { }
+ break;
+ case "txdToLoad":
+ txdToLoad = GetChildStringArray(child);
+ if (txdToLoad != null)
+ { }
+ break;
+ case "txdToUnload":
+ txdToUnload = GetChildStringArray(child);
+ if (txdToUnload != null)
+ { }
+ break;
+ case "residentResources":
+ residentResources = GetChildStringArray(child);
+ if (residentResources != null)
+ { }
+ break;
+ case "unregisterResources":
+ unregisterResources = GetChildStringArray(child);
+ if (unregisterResources != null)
+ { }
+ break;
+ case "mapChangeSetData":
+ mapChangeSetData = new List();
+ foreach (XmlNode mapChangeSetDataItem in child.ChildNodes)
+ {
+ mapChangeSetData.Add(new DlcContentChangeSet(mapChangeSetDataItem));
+ }
+ break;
+ case "associatedMap":
+ associatedMap = child.InnerText;
+ break;
+ case "requiresLoadingScreen":
+ requiresLoadingScreen = Xml.GetBoolAttribute(child, "value");
+ break;
+ case "loadingScreenContext":
+ loadingScreenContext = child.InnerText;
+ break;
+ case "useCacheLoader":
+ useCacheLoader = Xml.GetBoolAttribute(child, "value");
+ break;
+ case "executionConditions":
+ executionConditions = new DlcContentChangeSetExecutionConditions(child);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private List GetChildStringArray(XmlNode node)
+ {
+ if (!node.HasChildNodes) return null;
+ var result = new List();
+ foreach (XmlNode child in node.ChildNodes)
+ {
+ if (child.NodeType == XmlNodeType.Element)
+ {
+ result.Add(child.InnerText);
+ }
+ }
+ return result;
+ }
+
+ public override string ToString()
+ {
+ return (changeSetName != null) ? changeSetName : (associatedMap != null) ? associatedMap : null;
+ }
+
+ }
+
+ public class DlcContentChangeSetExecutionConditions
+ {
+ public string activeChangesetConditions { get; set; }
+ public string genericConditions { get; set; }
+
+ public DlcContentChangeSetExecutionConditions(XmlNode node)
+ {
+ Load(node);
+ }
+ public void Load(XmlNode node)
+ {
+ foreach (XmlNode child in node.ChildNodes)
+ {
+ if (child.NodeType != XmlNodeType.Element) continue;
+ switch (child.Name)
+ {
+ case "activeChangesetConditions":
+ activeChangesetConditions = child.InnerText;
+ break;
+ case "genericConditions":
+ genericConditions = child.InnerText;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ return (string.IsNullOrEmpty(activeChangesetConditions) ? "" : activeChangesetConditions + ", ") + (string.IsNullOrEmpty(genericConditions) ? "" : genericConditions);
+ }
+ }
+
+
+
+ public class DlcExtraFolderMountFile
+ {
+ public List FolderMounts { get; set; } = new List();
+
+ public void Load(XmlDocument doc)
+ {
+ var root = doc.DocumentElement;
+
+ XmlNodeList mountitems = doc.SelectNodes("SExtraFolderMountData/FolderMounts/Item");
+ FolderMounts.Clear();
+ for (int i = 0; i < mountitems.Count; i++)
+ {
+ var mount = new DlcExtraFolderMount();
+ mount.Init(mountitems[i]);
+ FolderMounts.Add(mount);
+ }
+
+ }
+
+ public override string ToString()
+ {
+ return "(" + FolderMounts.Count.ToString() + " FolderMounts)";
+ }
+ }
+
+ public class DlcExtraFolderMount
+ {
+ public string type { get; set; }
+ public string platform { get; set; }
+ public string path { get; set; }
+ public string mountAs { get; set; }
+
+ public void Init(XmlNode node)
+ {
+ type = Xml.GetStringAttribute(node, "type");
+ platform = Xml.GetStringAttribute(node, "platform");
+ path = Xml.GetChildInnerText(node, "path");
+ mountAs = Xml.GetChildInnerText(node, "mountAs");
+ }
+
+ public override string ToString()
+ {
+ return type + ": " + path + " - " + mountAs + ((platform != null) ? (" (" + platform + ")") : "");
+ }
+ }
+
+
+ public class DlcExtraTitleUpdateFile
+ {
+ public List Mounts { get; set; } = new List();
+
+ public void Load(XmlDocument doc)
+ {
+ var root = doc.DocumentElement;
+
+ XmlNodeList mountitems = doc.SelectNodes("SExtraTitleUpdateData/Mounts/Item");
+ Mounts.Clear();
+ for (int i = 0; i < mountitems.Count; i++)
+ {
+ var mount = new DlcExtraTitleUpdateMount();
+ mount.Init(mountitems[i]);
+ Mounts.Add(mount);
+ }
+
+ }
+
+ public override string ToString()
+ {
+ return "(" + Mounts.Count.ToString() + " Mounts)";
+ }
+ }
+
+ public class DlcExtraTitleUpdateMount
+ {
+ public string type { get; set; }
+ public string deviceName { get; set; }
+ public string path { get; set; }
+
+ public void Init(XmlNode node)
+ {
+ type = Xml.GetStringAttribute(node, "type");
+ deviceName = Xml.GetChildInnerText(node, "deviceName");
+ path = Xml.GetChildInnerText(node, "path");
+ }
+
+ public override string ToString()
+ {
+ return type + ": " + deviceName + " - " + path;
+ }
+ }
+
+
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/DlcSetupFile.cs b/CodeWalker.Core/GameFiles/FileTypes/DlcSetupFile.cs
new file mode 100644
index 0000000..43b47ee
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/DlcSetupFile.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace CodeWalker.GameFiles
+{
+ public class DlcSetupFile
+ {
+ public string deviceName { get; set; }
+ public string datFile { get; set; }
+ public string nameHash { get; set; }
+ public List contentChangeSetGroups { get; set; }
+ public string type { get; set; }
+ public string timeStamp { get; set; }
+ public int order { get; set; }
+ public int minorOrder { get; set; }
+ public int subPackCount { get; set; }
+ public bool isLevelPack { get; set; }
+
+ public RpfFile DlcFile { get; set; } //used by GameFileCache
+ public DlcContentFile ContentFile { get; set; }
+
+ public void Load(XmlDocument doc)
+ {
+
+ var root = doc.DocumentElement;
+ deviceName = Xml.GetChildInnerText(root, "deviceName");
+ datFile = Xml.GetChildInnerText(root, "datFile");
+ nameHash = Xml.GetChildInnerText(root, "nameHash");
+ type = Xml.GetChildInnerText(root, "type");
+ timeStamp = Xml.GetChildInnerText(root, "timeStamp");
+ order = Xml.GetIntAttribute(root.SelectSingleNode("order"), "value");
+ minorOrder = Xml.GetIntAttribute(root.SelectSingleNode("minorOrder"), "value");
+ subPackCount = Xml.GetIntAttribute(root.SelectSingleNode("subPackCount"), "value");
+ isLevelPack = Xml.GetBoolAttribute(root.SelectSingleNode("isLevelPack"), "value");
+
+ contentChangeSetGroups = new List();
+ var groups = root.SelectNodes("contentChangeSetGroups/Item");
+ foreach (XmlNode node in groups)
+ {
+ var group = new DlcSetupContentChangesetGroup();
+ group.Load(node);
+ contentChangeSetGroups.Add(group);
+ }
+
+ if (root.ChildNodes.Count > 15)
+ { }
+ }
+
+ public override string ToString()
+ {
+ return deviceName + ", " + datFile + ", " + nameHash + ", " + type + ", " + order.ToString() + ", " + ((contentChangeSetGroups != null) ? contentChangeSetGroups.Count.ToString() : "0") + " groups, " + timeStamp;
+ }
+ }
+
+ public class DlcSetupContentChangesetGroup
+ {
+ public string NameHash { get; set; }
+ public List ContentChangeSets { get; set; }
+
+ public void Load(XmlNode node)
+ {
+ if (node.ChildNodes.Count != 2)
+ { }
+ NameHash = Xml.GetChildInnerText(node, "NameHash");
+ ContentChangeSets = new List();
+ var changesets = node.SelectNodes("ContentChangeSets/Item");
+ foreach (XmlNode changeset in changesets)
+ {
+ ContentChangeSets.Add(changeset.InnerText);
+ }
+ }
+
+ public override string ToString()
+ {
+ return NameHash + " (" + ((ContentChangeSets != null) ? ContentChangeSets.Count.ToString() : "0") + " changesets)";
+ }
+ }
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/FxcFile.cs b/CodeWalker.Core/GameFiles/FileTypes/FxcFile.cs
new file mode 100644
index 0000000..4df16dd
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/FxcFile.cs
@@ -0,0 +1,1079 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+//GTAV FXC file reader by dexyfex.
+// use this however you want. some credit would be nice.
+
+namespace CodeWalker.GameFiles
+{
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class FxcFile : PackedFile
+ {
+ public string Name { get; set; }
+ public RpfFileEntry FileEntry { get; set; }
+ public uint Hash { get; set; }
+
+ public VertexType VertexType { get; set; }
+ public FxcHeaderExt[] Exts { get; set; }
+ public FxcHeaderChunk[] Chunks { get; set; }
+ public FxcShader[] Shaders { get; set; }
+ public FxcCBuffer[] CBuffers1 { get; set; }
+ public FxcVariable[] Variables1 { get; set; }
+ public FxcCBuffer[] CBuffers2 { get; set; }
+ public FxcVariable[] Variables2 { get; set; }
+ public FxcTechnique[] Techniques { get; set; }
+
+
+ public FxcShader[] VertexShaders { get; set; }
+ public FxcShader[] PixelShaders { get; set; }
+ public FxcShader[] ComputeShaders { get; set; }
+ public FxcShader[] DomainShaders { get; set; }
+ public FxcShader[] GeometryShaders { get; set; }
+ public FxcShader[] HullShaders { get; set; }
+
+ public Dictionary CBufferDict { get; set; }
+ public FxcVariable[] GlobalVariables { get; set; }
+
+
+ public string LastError { get; set; }
+
+
+
+
+
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ FileEntry = entry;
+ Name = entry.Name;
+ Hash = entry.ShortNameHash;
+
+ LastError = string.Empty;
+
+ MemoryStream ms = new MemoryStream(data);
+ BinaryReader br = new BinaryReader(ms);
+
+ uint magic_rgxe = br.ReadUInt32();
+ if(magic_rgxe!= 1702389618) //"rgxe"
+ {
+ return;
+ }
+
+ VertexType = (VertexType)br.ReadUInt32();
+
+ byte ec0 = br.ReadByte();
+ var exts1 = new List();
+ for (int e = 0; e < ec0; e++)
+ {
+ FxcHeaderExt ext = new FxcHeaderExt();
+ ext.Name = ReadString(br);
+ ext.Unk0Byte = br.ReadByte(); //0
+ ext.Unk1Uint = br.ReadUInt32(); //2
+ exts1.Add(ext);
+ }
+ Exts = exts1.ToArray();
+
+
+ List[] shadergrps = new List[6];
+
+ var chunks = new List();
+ var shaders = new List();
+ int gindex = 0;
+ while (gindex<6)// (sc0 > 0)
+ {
+ var shadergrp = new List();
+ shadergrps[gindex] = shadergrp;
+
+ gindex++;
+ byte sc0 = br.ReadByte();
+ if (sc0 == 0)
+ {
+ sc0 = br.ReadByte(); //this is a little odd, sometimes a byte skip
+ }
+ FxcHeaderChunk chunk = new FxcHeaderChunk();
+ chunk.Read(br);
+ chunk.Gindex = gindex;
+ chunk.ShaderCount = sc0;
+ chunks.Add(chunk);
+ for (int s = 1; s < sc0; s++)
+ {
+ bool exbyteflag1 = (gindex==5); //GS seems to be in diff format??
+ bool vsgsps = (gindex == 1) || (gindex == 2) || (gindex == 5);
+ FxcShader shader = new FxcShader();
+ if (!shader.Read(br, exbyteflag1, vsgsps))
+ {
+ LastError += shader.LastError;
+ //gindex = 6; //get outta the loop?
+ //break;
+ }
+ shaders.Add(shader);
+ shadergrp.Add(shader);
+ }
+ }
+
+ Shaders = shaders.ToArray();
+ VertexShaders = shadergrps[0]?.ToArray();
+ PixelShaders = shadergrps[1]?.ToArray();
+ ComputeShaders = shadergrps[2]?.ToArray();
+ DomainShaders = shadergrps[3]?.ToArray();
+ GeometryShaders = shadergrps[4]?.ToArray();
+ HullShaders = shadergrps[5]?.ToArray();
+
+
+ Chunks = chunks.ToArray();
+
+ //ms.Dispose();
+ //return;
+
+ List globalVars = new List();
+ CBufferDict = new Dictionary();
+ FxcCBuffer cbtmp = null;
+
+ try //things can be uncertain after this...
+ {
+
+ byte cbCount1 = br.ReadByte();
+ if (cbCount1 > 0)
+ {
+ var cbuffers1 = new List();
+ for (int i = 0; i < cbCount1; i++) //cbuffers? includes?
+ {
+ FxcCBuffer cbuf = new FxcCBuffer();
+ cbuf.Read(br);
+ cbuffers1.Add(cbuf);
+ CBufferDict[cbuf.NameHash] = cbuf;
+ }
+ CBuffers1 = cbuffers1.ToArray();
+ }
+
+ byte varCount1 = br.ReadByte();
+ if (varCount1 > 0)
+ {
+ var vars1 = new List(); //cbuffer contents/vars
+ for (int i = 0; i < varCount1; i++)
+ {
+ FxcVariable vari = new FxcVariable();
+ vari.Read(br);
+ vars1.Add(vari);
+ if (CBufferDict.TryGetValue(vari.CBufferName, out cbtmp))
+ {
+ cbtmp.AddVariable(vari);
+ }
+ else
+ {
+ globalVars.Add(vari);
+ }
+ }
+ Variables1 = vars1.ToArray();
+ }
+
+
+ byte cbCount2 = br.ReadByte(); //0,1, +?
+ if (cbCount2 > 0)
+ {
+ var cbuffers2 = new List(); //more cbuffers..
+ for (int i = 0; i < cbCount2; i++)
+ {
+ FxcCBuffer cbuf = new FxcCBuffer();
+ cbuf.Read(br);
+ cbuffers2.Add(cbuf);
+ CBufferDict[cbuf.NameHash] = cbuf;
+ }
+ CBuffers2 = cbuffers2.ToArray();
+ }
+
+
+ byte varCount2 = br.ReadByte();
+ if (varCount2 > 0)
+ {
+ var vars2 = new List();
+ for (int i = 0; i < varCount2; i++) //textures/samplers
+ {
+ FxcVariable vari = new FxcVariable();
+ vari.Read(br);
+ vars2.Add(vari);
+ if (CBufferDict.TryGetValue(vari.CBufferName, out cbtmp))
+ {
+ cbtmp.AddVariable(vari);
+ }
+ else
+ {
+ globalVars.Add(vari);
+ }
+ }
+ Variables2 = vars2.ToArray();
+ }
+
+
+ byte techCount = br.ReadByte();
+ if (techCount > 0)
+ {
+ var techniques = new List();
+ for (int i = 0; i < techCount; i++)
+ {
+ FxcTechnique tech = new FxcTechnique();
+ tech.Read(br);
+ techniques.Add(tech);
+ }
+ Techniques = techniques.ToArray();
+ }
+
+
+ foreach (var cbuf in CBufferDict.Values)
+ {
+ cbuf.ConsolidateVariables();
+ }
+ GlobalVariables = globalVars.ToArray();
+
+
+ if (ms.Position != ms.Length)
+ { }
+
+ }
+ catch (Exception ex)
+ {
+ LastError = ex.ToString();
+ }
+
+ ms.Dispose();
+ }
+
+
+
+ public string GetMetaString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine("Name: " + Name);
+ sb.AppendLine("Hash: " + Hash.ToString());
+ sb.AppendLine("Vertex type: " + ((uint)VertexType).ToString());
+ sb.AppendLine();
+
+ if (Exts != null)
+ {
+ sb.AppendLine("Header");
+ foreach (var ext in Exts)
+ {
+ sb.AppendLine(" " + ext.ToString());
+ }
+ sb.AppendLine();
+ }
+
+ if (Chunks != null)
+ {
+ sb.AppendLine("Sections");
+ foreach (var chunk in Chunks)
+ {
+ sb.AppendLine(" " + chunk.ToString());
+ }
+ sb.AppendLine();
+ }
+
+ if (Shaders != null)
+ {
+ sb.AppendLine("Shaders (" + Shaders.Length.ToString() + ")");
+ foreach (var shader in Shaders)
+ {
+ sb.AppendLine(" " + shader.Name);
+ if ((shader.Params != null) && (shader.Params.Length > 0))
+ {
+ sb.AppendLine(" (Params)");
+ foreach (var parm in shader.Params)
+ {
+ sb.AppendLine(" " + parm);
+ }
+ }
+ if ((shader.Buffers != null) && (shader.Buffers.Length > 0))
+ {
+ sb.AppendLine(" (Buffers)");
+ foreach (var ext in shader.Buffers)
+ {
+ sb.AppendLine(" " + ext.ToString());
+ }
+ }
+ }
+ sb.AppendLine();
+ }
+
+ if (CBuffers1 != null)
+ {
+ sb.AppendLine("CBuffers 1 (" + CBuffers1.Length.ToString() + ")");
+ foreach (var p in CBuffers1)
+ {
+ sb.AppendLine(" " + p.ToString());
+ }
+ sb.AppendLine();
+ }
+
+ if (Variables1 != null)
+ {
+ sb.AppendLine("Variables 1 (" + Variables1.Length.ToString() + ")");
+ foreach (var p in Variables1)
+ {
+ sb.AppendLine(" " + p.ToString());
+ if ((p.Params != null) && (p.Params.Length > 0))
+ {
+ sb.AppendLine(" (Params)");
+ foreach (var c in p.Params)
+ {
+ sb.AppendLine(" " + c.ToString());
+ }
+ }
+ if ((p.Values != null) && (p.Values.Length > 0))
+ {
+ sb.AppendLine(" (Values)");
+ foreach (var d in p.Values)
+ {
+ sb.AppendLine(" " + d.ToString());
+ }
+ }
+ }
+ sb.AppendLine();
+ }
+
+ if (CBuffers2 != null)
+ {
+ sb.AppendLine("CBuffers 2 (" + CBuffers2.Length.ToString() + ")");
+ foreach (var p in CBuffers2)
+ {
+ sb.AppendLine(" " + p.ToString());
+ }
+ sb.AppendLine();
+ }
+
+ if (Variables2 != null)
+ {
+ sb.AppendLine("Variables 2 (" + Variables2.Length.ToString() + ")");
+ foreach (var p in Variables2)
+ {
+ sb.AppendLine(" " + p.ToString());
+ if ((p.Params != null) && (p.Params.Length > 0))
+ {
+ sb.AppendLine(" (Params)");
+ foreach (var c in p.Params)
+ {
+ sb.AppendLine(" " + c.ToString());
+ }
+ }
+ if ((p.Values != null) && (p.Values.Length > 0))
+ {
+ sb.AppendLine(" (Values)");
+ foreach (var d in p.Values)
+ {
+ sb.AppendLine(" " + d.ToString());
+ }
+ }
+ }
+ sb.AppendLine();
+ }
+
+ if (Techniques != null)
+ {
+ sb.AppendLine("Techniques (" + Techniques.Length.ToString() + ")");
+ foreach (var t in Techniques)
+ {
+ sb.AppendLine(" " + t.Name);
+ if ((t.PassCount > 0) && (t.Passes != null))
+ {
+ sb.AppendLine(" (Passes)");
+ foreach (var p in t.Passes)
+ {
+ sb.AppendLine(" " + p.ToString());
+ if ((p.ParamCount > 0) && (p.Params != null))
+ {
+ //sb.AppendLine(" Params");
+ foreach (var v in p.Params)
+ {
+ sb.AppendLine(" " + v.ToString());
+ }
+ }
+ }
+ }
+ }
+ sb.AppendLine();
+ }
+
+ return sb.ToString();
+ }
+
+
+ public FxcShader GetVS(int id)
+ {
+ int i = id - 1;
+ if ((i < 0) || (VertexShaders == null) || (i >= VertexShaders.Length))
+ {
+ return null;
+ }
+ return VertexShaders[i];
+ }
+ public FxcShader GetPS(int id)
+ {
+ int i = id - 1;
+ if ((i < 0) || (PixelShaders == null) || (i >= PixelShaders.Length))
+ {
+ return null;
+ }
+ return PixelShaders[i];
+ }
+ public FxcShader GetCS(int id)
+ {
+ int i = id - 1;
+ if ((i < 0) || (ComputeShaders == null) || (i >= ComputeShaders.Length))
+ {
+ return null;
+ }
+ return ComputeShaders[i];
+ }
+ public FxcShader GetDS(int id)
+ {
+ int i = id - 1;
+ if ((i < 0) || (DomainShaders == null) || (i >= DomainShaders.Length))
+ {
+ return null;
+ }
+ return DomainShaders[i];
+ }
+ public FxcShader GetGS(int id)
+ {
+ int i = id - 1;
+ if ((i < 0) || (GeometryShaders == null) || (i >= GeometryShaders.Length))
+ {
+ return null;
+ }
+ return GeometryShaders[i];
+ }
+ public FxcShader GetHS(int id)
+ {
+ int i = id - 1;
+ if ((i < 0) || (HullShaders == null) || (i >= HullShaders.Length))
+ {
+ return null;
+ }
+ return HullShaders[i];
+ }
+
+
+
+
+ public static string ReadString(BinaryReader br)
+ {
+ byte sl = br.ReadByte();
+ if (sl == 0) return string.Empty;
+ byte[] ba = new byte[sl];
+ br.Read(ba, 0, sl);
+ return (sl > 1) ? ASCIIEncoding.ASCII.GetString(ba, 0, sl - 1) : string.Empty;
+ }
+ public static string[] ReadStringArray(BinaryReader br)
+ {
+ string[] r = null;
+
+ byte sc = br.ReadByte();
+ if (sc > 0)
+ {
+ StringBuilder sb = new StringBuilder();
+ r = new string[sc];
+ for (int i = 0; i < sc; i++)
+ {
+ sb.Clear();
+ byte sl = br.ReadByte();
+ for (int n = 0; n < sl; n++)
+ {
+ sb.Append((char)br.ReadByte());
+ }
+ r[i] = sb.ToString().Replace("\0", "");
+ }
+ }
+
+ return r;
+ }
+
+ }
+
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class FxcHeaderExt
+ {
+ public string Name { get; set; }
+ public byte Unk0Byte { get; set; }
+ public uint Unk1Uint { get; set; }
+
+ public override string ToString()
+ {
+ return Name + ": " + Unk0Byte.ToString() + ": " + Unk1Uint.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class FxcHeaderChunk
+ {
+ public string Name { get; set; }
+ public byte Unk1Byte { get; set; }
+ public byte Unk2Byte { get; set; }
+ public uint Unk3Uint { get; set; }
+ public int Gindex { get; set; } //index in the fxc file
+ public byte ShaderCount { get; set; } //number of shaders in the section
+
+ public void Read(BinaryReader br)
+ {
+ Name = FxcFile.ReadString(br); //usually "NULL"
+ Unk1Byte = br.ReadByte();
+ Unk2Byte = br.ReadByte();
+ Unk3Uint = br.ReadUInt32();
+ }
+
+ public override string ToString()
+ {
+ return Name + ": " + Gindex.ToString() + ": " + ShaderCount.ToString() + ": " + Unk1Byte.ToString() + ": " + Unk2Byte.ToString() + ": " + Unk3Uint.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class FxcShader
+ {
+ public long Offset { get; set; }
+ public string Name { get; set; }
+ public string[] Params { get; set; }
+ public FxcShaderBufferRef[] Buffers { get; set; }//CBuffers
+ public byte VersionMajor { get; set; }
+ public byte VersionMinor { get; set; }
+ public string VersionString { get; set; }
+ public byte[] ByteCode { get; set; }
+ //public ShaderBytecode ByteCodeObj { get; set; }
+ //public ShaderProfile ShaderProfile { get; set; }
+ public string Disassembly { get; set; }
+ public string LastError { get; set; }
+
+ public bool Read(BinaryReader br, bool exbyteflag, bool vsgsps)
+ {
+ Offset = br.BaseStream.Position;
+
+ Name = FxcFile.ReadString(br);
+
+ if (Name.Length == 0)
+ {
+ Name = FxcFile.ReadString(br); //why (seems to be GS only)
+ exbyteflag = true;
+ }
+
+
+ Params = FxcFile.ReadStringArray(br);
+
+ byte bufferCount = br.ReadByte();
+ var buffers = new List();
+ for (int e = 0; e < bufferCount; e++)
+ {
+ FxcShaderBufferRef ext = new FxcShaderBufferRef();
+ ext.Name = FxcFile.ReadString(br);
+ ext.Unk0Ushort = br.ReadUInt16();
+ buffers.Add(ext);
+ }
+ Buffers = buffers.ToArray();
+
+ byte exbyte = 0;
+ if (exbyteflag)
+ {
+ exbyte = br.ReadByte(); //not sure what this is used for...
+ if ((exbyte != 0))
+ { }
+ }
+
+
+ uint datalength = br.ReadUInt32();
+
+ if (datalength > 0)
+ {
+ uint magic_dxbc = br.ReadUInt32();
+ if (magic_dxbc != 1128421444) //"DXBC" - directx bytecode header
+ {
+ LastError += "Unexpected data found at DXBC header...\r\n";
+ return false; //didn't find the DXBC header... abort!
+ }
+ br.BaseStream.Position -= 4; //wind back because dx needs that header
+
+ ByteCode = br.ReadBytes((int)datalength);
+
+ if (vsgsps)
+ {
+ VersionMajor = br.ReadByte();//4,5 //appears to be shader model version
+ VersionMinor = br.ReadByte(); //perhaps shader minor version
+ }
+
+ //try
+ //{
+ // ByteCodeObj = new ShaderBytecode(ByteCode);
+ // ShaderProfile = ByteCodeObj.GetVersion();
+ // switch (ShaderProfile.Version)
+ // {
+ // case ShaderVersion.VertexShader:
+ // case ShaderVersion.PixelShader:
+ // case ShaderVersion.GeometryShader:
+ // VersionMajor = br.ReadByte();//4,5 //appears to be shader model version
+ // VersionMinor = br.ReadByte(); //perhaps shader minor version
+ // break;
+ // default:
+ // VersionMajor = (byte)ShaderProfile.Major;
+ // VersionMinor = (byte)ShaderProfile.Minor;
+ // break;
+ // }
+ // //do disassembly last, so any errors won't cause the file read to break
+ // Disassembly = ByteCodeObj.Disassemble();
+ //}
+ //catch (Exception ex)
+ //{
+ // LastError += ex.ToString() + "\r\n";
+ // return false;
+ //}
+ }
+ else
+ {
+ }
+ return true;
+ }
+
+
+
+ public override string ToString()
+ {
+ return Name;
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class FxcShaderBufferRef
+ {
+ public string Name { get; set; } //Buffer name
+ public ushort Unk0Ushort { get; set; }
+
+ public override string ToString()
+ {
+ return Name + ": " + Unk0Ushort.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class FxcCBuffer
+ {
+ public uint u003 { get; set; }
+ public ushort us001 { get; set; }
+ public ushort us002 { get; set; }
+ public ushort us003 { get; set; }
+ public ushort us004 { get; set; }
+ public ushort us005 { get; set; }
+ public ushort us006 { get; set; }
+ public string Name { get; set; }
+
+ public uint NameHash { get { return JenkHash.GenHash(Name); } }
+ public List VariablesList;
+ public FxcVariable[] Variables { get; set; }
+
+ public void Read(BinaryReader br)
+ {
+ u003 = br.ReadUInt32(); //176, 16 //256
+ us001 = br.ReadUInt16(); //6, 5
+ us002 = br.ReadUInt16(); //6, 12
+ us003 = br.ReadUInt16(); //6, 5
+ us004 = br.ReadUInt16(); //6, 5
+ us005 = br.ReadUInt16(); //6, 5
+ us006 = br.ReadUInt16(); //6, 5
+ Name = FxcFile.ReadString(br); // _locals //"rage_matrices", "misc_globals", "lighting_globals", "more_stuff"
+ JenkIndex.Ensure(Name); //why not :P
+ }
+
+ public override string ToString()
+ {
+ return Name + ": " + u003 + ", " + us001 + ", " + us002 + ", " + us003 + ", " + us004 + ", " + us005 + ", " + us006;
+ }
+
+ public void AddVariable(FxcVariable vari)
+ {
+ if (VariablesList == null) VariablesList = new List();
+ VariablesList.Add(vari);
+ }
+ public void ConsolidateVariables()
+ {
+ if (VariablesList != null)
+ {
+ Variables = VariablesList.ToArray();
+ VariablesList = null;
+ }
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class FxcVariable
+ {
+ public byte b0 { get; set; }
+ public byte b1 { get; set; }
+ public byte b2 { get; set; }
+ public byte b3 { get; set; }
+ public string Name1 { get; set; }
+ public string Name2 { get; set; }
+ public byte b4 { get; set; }
+ public byte b5 { get; set; }
+ public byte b6 { get; set; }
+ public byte b7 { get; set; }
+ public MetaHash CBufferName { get; set; }
+ public byte ParamCount { get; set; }
+ public FxcVariableParam[] Params { get; set; }
+ public byte ValueCount { get; set; }
+ public float[] Values { get; set; }
+
+
+ public void Read(BinaryReader br)
+ {
+ b0 = br.ReadByte(); //5
+ b1 = br.ReadByte(); //0,1
+ b2 = br.ReadByte(); //19 //17
+ b3 = br.ReadByte(); //2 //27
+ Name1 = FxcFile.ReadString(br);
+ Name2 = FxcFile.ReadString(br);
+ b4 = br.ReadByte(); //32
+ b5 = br.ReadByte(); //
+ b6 = br.ReadByte(); //
+ b7 = br.ReadByte(); //
+ CBufferName = br.ReadUInt32(); //hash
+
+
+ ParamCount = br.ReadByte(); //1
+ if (ParamCount > 0)
+ {
+ List parms = new List();
+ for (int i = 0; i < ParamCount; i++)
+ {
+ FxcVariableParam parm = new FxcVariableParam();
+ parm.Read(br);
+ parms.Add(parm);
+ }
+ Params = parms.ToArray();
+ }
+
+ ValueCount = br.ReadByte();
+ if (ValueCount > 0)
+ {
+ Values = new float[ValueCount];
+ for (int i = 0; i < ValueCount; i++)
+ {
+ Values[i] = br.ReadSingle(); //TODO: how to know what types to use?
+ }
+ }
+
+
+ #region debug validation
+
+ switch (b0)
+ {
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 11:
+ case 14:
+ case 17:
+ case 20:
+ break;
+ case 15:
+ case 18:
+ case 19:
+ case 21:
+ case 22:
+ break;
+ default:
+ break;
+ }
+
+ switch (b1)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ case 12:
+ case 254:
+ case 255:
+ break;
+ case 14:
+ case 16:
+ case 24:
+ case 40:
+ case 117:
+ break;
+ default:
+ break;
+ }
+
+ switch (CBufferName)
+ {
+ case 2165770756:
+ case 3458315595:
+ case 1059338858:
+ case 3779850771:
+ case 2988188919:
+ case 713939274:
+ case 3369176576:
+ case 1927486087:
+ case 4135739982:
+ case 1032736383:
+ case 3084860475:
+ case 3844532749:
+ case 3635751376:
+ case 2172777116:
+ case 4255900365:
+ case 2725372071:
+ case 3661207622:
+ case 4213364555:
+ case 1519748817:
+ case 118958736:
+ case 2397841684:
+ case 1340365912:
+ case 2531859994:
+ case 0:
+ break;
+ case 482774302:
+ case 2300268537:
+ case 2714887816:
+ case 2049538169:
+ case 1316881611:
+ case 3367312321:
+ case 4017086211:
+ case 3743503190:
+ case 938670440:
+ case 2782027784:
+ case 2865440919:
+ case 2384984532:
+ case 486482914:
+ case 3162602184:
+ case 1834530379:
+ case 1554708878:
+ case 1142002603:
+ case 3049310097:
+ case 764124013:
+ case 2526104914:
+ case 1561144077:
+ case 970248680:
+ case 3899781660:
+ case 1853474951:
+ case 2224880237:
+ case 3766848419:
+ case 2718810031:
+ case 115655537:
+ case 4224116138:
+ case 3572088685:
+ case 1438660507:
+ case 4092686193:
+ case 871214106:
+ case 2121263542:
+ case 3502503908:
+ case 586499600:
+ case 4046148196:
+ case 2999112456:
+ case 2355014976:
+ case 579364910:
+ case 2193272593:
+ case 1641847936:
+ case 1286180849:
+ case 3291504934:
+ case 278397346:
+ case 3346871633:
+ case 4091106477:
+ case 832855465:
+ case 3616072140:
+ case 3977262900:
+ case 2062541604:
+ case 950211059:
+ case 2380663322:
+ case 2783177544:
+ case 1100625170:
+ case 1279142172:
+ case 1004646027:
+ case 2092585241:
+ case 4165560568:
+ case 2651790209:
+ case 3453406875:
+ case 488789527:
+ case 3375019131:
+ case 519785780:
+ case 729415208:
+ case 556501613:
+ case 2829744882:
+ case 1778702372:
+ case 2564407213:
+ case 3291498326:
+ case 1275817784:
+ case 962813362:
+ case 2020034807:
+ case 2017746823:
+ case 1237102223:
+ case 4029270406:
+ case 673228990:
+ case 201854132:
+ case 1866965008:
+ case 957783816:
+ case 2522030664:
+ case 1910375705:
+ case 2656344872:
+ break;
+ default:
+ break;
+ }
+
+ switch (b3)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 10:
+ case 11:
+ case 17:
+ case 19:
+ case 27:
+ case 34:
+ break;
+ case 6:
+ case 16:
+ case 26:
+ case 32:
+ case 33:
+ case 35:
+ case 39:
+ case 49:
+ case 51:
+ break;
+ default:
+ break;
+ }
+
+
+ #endregion
+
+
+ }
+
+ public override string ToString()
+ {
+ return b0.ToString() + ", " + b1.ToString() + ", " + b2.ToString() + ", " + b3.ToString() + ", " + b4.ToString() + ", " + b5.ToString() + ", " + b6.ToString() + ", " + b7.ToString() + ", " + CBufferName.ToString() + ", " + Name1 + ", " + Name2;
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class FxcVariableParam
+ {
+ public string Name { get; set; }
+ public byte Type { get; set; }
+ public object Value { get; set; }
+
+ public void Read(BinaryReader br)
+ {
+ Name = FxcFile.ReadString(br);
+ Type = br.ReadByte();
+ switch (Type)
+ {
+ case 0:
+ Value = br.ReadUInt32();
+ break;
+ case 1:
+ Value = br.ReadSingle();
+ break;
+ case 2:
+ Value = FxcFile.ReadString(br);
+ break;
+ default:
+ break;
+ }
+ }
+
+ public override string ToString()
+ {
+ return Name + ": " + Value?.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class FxcTechnique
+ {
+ public string Name { get; set; }
+ public byte PassCount { get; set; }
+ public FxcPass[] Passes { get; set; }
+
+ public void Read(BinaryReader br)
+ {
+ Name = FxcFile.ReadString(br); //"draw", "deferred_draw", etc..
+
+ PassCount = br.ReadByte();//
+ if (PassCount > 0)
+ {
+ Passes = new FxcPass[PassCount];
+ for (int i = 0; i < PassCount; i++)
+ {
+ FxcPass p = new FxcPass();
+ p.Read(br);
+ Passes[i] = p;
+ }
+ }
+
+ }
+
+ public override string ToString()
+ {
+ return Name + " (" + PassCount.ToString() + " pass" + (PassCount != 1 ? "es" : "") + ")";
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class FxcPass
+ {
+ public byte VS { get; set; }
+ public byte PS { get; set; }
+ public byte CS { get; set; }
+ public byte DS { get; set; }
+ public byte GS { get; set; }
+ public byte HS { get; set; }
+ public byte ParamCount { get; set; }
+ public FxcPassParam[] Params { get; set; }
+
+ public void Read(BinaryReader br)
+ {
+ VS = br.ReadByte();
+ PS = br.ReadByte();
+ CS = br.ReadByte();
+ DS = br.ReadByte();
+ GS = br.ReadByte();
+ HS = br.ReadByte();
+
+ ParamCount = br.ReadByte();
+ if (ParamCount > 0)
+ {
+ Params = new FxcPassParam[ParamCount];
+ for (int i = 0; i < ParamCount; i++)
+ {
+ FxcPassParam p = new FxcPassParam();
+ p.u0 = br.ReadUInt32();
+ p.u1 = br.ReadUInt32();
+ Params[i] = p;
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ return VS.ToString() + ", " + PS.ToString() + ", " + CS.ToString() + ", " + DS.ToString() + ", " + GS.ToString() + ", " + HS.ToString();
+ }
+
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class FxcPassParam
+ {
+ public uint u0 { get; set; }
+ public uint u1 { get; set; }
+
+ public override string ToString()
+ {
+ return u0.ToString() + ", " + u1.ToString();
+ }
+ }
+
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/GtxdFile.cs b/CodeWalker.Core/GameFiles/FileTypes/GtxdFile.cs
new file mode 100644
index 0000000..53dcaf3
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/GtxdFile.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace CodeWalker.GameFiles
+{
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class GtxdFile : GameFile, PackedFile
+ {
+
+ public RbfFile Rbf { get; set; }
+
+
+ public Dictionary CMapParentTxds { get; set; }
+
+
+
+
+ public GtxdFile() : base(null, GameFileType.Gtxd)
+ {
+ }
+ public GtxdFile(RpfFileEntry entry) : base(entry, GameFileType.Gtxd)
+ {
+ }
+
+
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ RpfFileEntry = entry;
+ Name = entry.Name;
+ FilePath = Name;
+
+
+ if (entry.NameLower.EndsWith(".ymt"))
+ {
+ MemoryStream ms = new MemoryStream(data);
+ if (RbfFile.IsRBF(ms))
+ {
+ Rbf = new RbfFile();
+ var rbfstruct = Rbf.Load(ms);
+
+ if (rbfstruct.Name == "CMapParentTxds")
+ {
+ LoadMapParentTxds(rbfstruct);
+ }
+
+ Loaded = true;
+ return;
+ }
+ else
+ {
+ //not an RBF file...
+ }
+ }
+ else if (entry.NameLower.EndsWith(".meta"))
+ {
+ //required for update\x64\dlcpacks\mpheist\dlc.rpf\common\data\gtxd.meta and update\x64\dlcpacks\mpluxe\dlc.rpf\common\data\gtxd.meta
+ string bom = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
+ string xml = Encoding.UTF8.GetString(data);
+
+ if (xml.StartsWith(bom, StringComparison.Ordinal))
+ {
+ xml = xml.Remove(0, bom.Length);
+ }
+
+ LoadMapParentTxds(xml);
+ Loaded = true;
+ }
+
+
+ }
+
+
+ private void LoadMapParentTxds(RbfStructure rbfstruct)
+ {
+
+ CMapParentTxds = new Dictionary();
+ //StringBuilder sblist = new StringBuilder();
+ foreach (var child in rbfstruct.Children)
+ {
+ var childstruct = child as RbfStructure;
+ if ((childstruct != null) && (childstruct.Name == "txdRelationships"))
+ {
+ foreach (var txdrel in childstruct.Children)
+ {
+ var txdrelstruct = txdrel as RbfStructure;
+ if ((txdrelstruct != null) && (txdrelstruct.Name == "item"))
+ {
+ string parentstr = string.Empty;
+ string childstr = string.Empty;
+ foreach (var item in txdrelstruct.Children)
+ {
+ var itemstruct = item as RbfStructure;
+ if ((itemstruct != null))
+ {
+ var strbytes = itemstruct.Children[0] as RbfBytes;
+ string thisstr = string.Empty;
+ if (strbytes != null)
+ {
+ thisstr = Encoding.ASCII.GetString(strbytes.Value).Replace("\0", "");
+ }
+ switch (item.Name)
+ {
+ case "parent":
+ parentstr = thisstr;
+ break;
+ case "child":
+ childstr = thisstr;
+ break;
+ }
+ }
+
+ }
+ if ((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
+ {
+ if (!CMapParentTxds.ContainsKey(childstr))
+ {
+ CMapParentTxds.Add(childstr, parentstr);
+ }
+ else
+ {
+ }
+ //sblist.AppendLine(childstr + ": " + parentstr);
+ }
+ }
+ }
+ }
+ }
+ //string alltxdmap = sblist.ToString();
+ //if (!string.IsNullOrEmpty(alltxdmap))
+ //{
+ //}
+
+ }
+
+
+
+ private void LoadMapParentTxds(string xml)
+ {
+ XmlDocument xmldoc = new XmlDocument();
+ xmldoc.LoadXml(xml); //maybe better load xml.ToLower() and use "cmapparenttxds/txdrelationships/item" as xpath?
+ XmlNodeList items = xmldoc.SelectNodes("CMapParentTxds/txdRelationships/Item | CMapParentTxds/txdRelationships/item");
+
+ CMapParentTxds = new Dictionary();
+ for (int i = 0; i < items.Count; i++)
+ {
+ string parentstr = Xml.GetChildInnerText(items[i], "parent");
+ string childstr = Xml.GetChildInnerText(items[i], "child");
+
+ if ((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
+ {
+ if (!CMapParentTxds.ContainsKey(childstr))
+ {
+ CMapParentTxds.Add(childstr, parentstr);
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/Gxt2File.cs b/CodeWalker.Core/GameFiles/FileTypes/Gxt2File.cs
new file mode 100644
index 0000000..016f01e
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/Gxt2File.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class Gxt2File : PackedFile
+ {
+ public string Name { get; set; }
+ public RpfFileEntry FileEntry { get; set; }
+ public uint EntryCount { get; set; }
+ public Gxt2Entry[] TextEntries { get; set; }
+ //public Dictionary Dict { get; set; }
+
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+ FileEntry = entry;
+ //Dict = new Dictionary();
+
+ using (BinaryReader br = new BinaryReader(new MemoryStream(data)))
+ {
+ uint gxt2 = br.ReadUInt32(); //"GXT2" - 1196971058
+ if (gxt2 != 1196971058)
+ { return; }
+
+ EntryCount = br.ReadUInt32();
+ TextEntries = new Gxt2Entry[EntryCount];
+ for (uint i = 0; i < EntryCount; i++)
+ {
+ var e = new Gxt2Entry();
+ e.Hash = br.ReadUInt32();
+ e.Offset = br.ReadUInt32();
+ TextEntries[i] = e;
+ }
+
+ gxt2 = br.ReadUInt32(); //another "GXT2"
+ if (gxt2 != 1196971058)
+ { return; }
+
+ uint endpos = br.ReadUInt32();
+
+ List buf = new List();
+
+ for (uint i = 0; i < EntryCount; i++)
+ {
+ var e = TextEntries[i];
+ br.BaseStream.Position = e.Offset;
+
+ buf.Clear();
+ byte b = br.ReadByte();
+ while ((b != 0) && (br.BaseStream.Position Index = new Dictionary();
+ private static object syncRoot = new object();
+
+ public static volatile bool FullIndexBuilt = false;
+
+ public static void Clear()
+ {
+ lock (syncRoot)
+ {
+ Index.Clear();
+ }
+ }
+
+ public static bool Ensure(string str)
+ {
+ uint hash = JenkHash.GenHash(str);
+ if (hash == 0) return true;
+ lock (syncRoot)
+ {
+ if (!Index.ContainsKey(hash))
+ {
+ Index.Add(hash, str);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static bool Ensure(string str, uint hash)
+ {
+ if (hash == 0) return true;
+ lock (syncRoot)
+ {
+ if (!Index.ContainsKey(hash))
+ {
+ Index.Add(hash, str);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static string GetString(uint hash)
+ {
+ string res;
+ lock (syncRoot)
+ {
+ if (!Index.TryGetValue(hash, out res))
+ {
+ res = hash.ToString();
+ }
+ }
+ return res;
+ }
+ public static string TryGetString(uint hash)
+ {
+ string res;
+ lock (syncRoot)
+ {
+ if (!Index.TryGetValue(hash, out res))
+ {
+ res = string.Empty;
+ }
+ }
+ return res;
+ }
+
+ }
+
+
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/JPsoFile.cs b/CodeWalker.Core/GameFiles/FileTypes/JPsoFile.cs
new file mode 100644
index 0000000..8062e5f
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/JPsoFile.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ public class JPsoFile : PackedFile
+ {
+ public RpfFileEntry FileEntry { get; set; }
+ public PsoFile Pso { get; set; }
+
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ //MemoryStream ms = new MemoryStream(data);
+
+ FileEntry = entry;
+
+ MemoryStream ms = new MemoryStream(data);
+
+ if (PsoFile.IsPSO(ms))
+ {
+ Pso = new PsoFile();
+ Pso.Load(ms);
+
+ //PsoTypes.EnsurePsoTypes(Pso);
+
+ var root = PsoTypes.GetRootEntry(Pso);
+ if (root != null)
+ {
+ }
+ return;
+ }
+ else
+ {
+
+ }
+
+
+
+
+ }
+
+ }
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs b/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs
new file mode 100644
index 0000000..7274b2b
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs
@@ -0,0 +1,3034 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TC = System.ComponentModel.TypeConverterAttribute;
+using EXP = System.ComponentModel.ExpandableObjectConverter;
+using SharpDX;
+
+
+
+
+/*
+
+Parts of this are adapted from CamxxCore's RageAudioTool, although it's been completely reworked for CW.
+-dexyfex
+
+
+https://github.com/CamxxCore/RageAudioTool
+
+MIT License
+
+Copyright (c) 2017 Cameron Berry
+
+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.
+
+*/
+
+
+
+namespace CodeWalker.GameFiles
+{
+ [TC(typeof(EXP))] public class RelFile : PackedFile
+ {
+ public RpfFileEntry FileEntry { get; set; }
+ public string Name { get; set; }
+
+ public uint Type { get; set; }
+ public uint DataLength { get; set; }
+ public byte[] DataBlock { get; set; }
+ public uint DataUnkVal { get; set; }
+ public uint NameTableLength { get; set; }
+ public uint NameTableCount { get; set; }
+ public uint[] NameTableOffsets { get; set; }
+ public string[] NameTable { get; set; }
+ public uint IndexCount { get; set; }
+ public uint IndexStringFlags { get; set; }
+ public RelIndexHash[] IndexHashes { get; set; }
+ public RelIndexString[] IndexStrings { get; set; }
+ public uint Unk05Count { get; set; }
+ public uint[] Unk05Arr { get; set; }
+ public MetaHash[] Unk05Hashes { get; set; }
+ public uint ContainerCount { get; set; }
+ public uint[] ContainerUnkArr { get; set; }
+ public MetaHash[] ContainerHashes { get; set; }
+
+ public RelData[] RelDatas { get; set; }
+ public RelData[] RelDatasSorted { get; set; }
+ //testing zone for decoding .rel audio files.
+
+ public Dictionary RelDataDict { get; set; } = new Dictionary();
+
+
+ public RelFile()
+ {
+ }
+ public RelFile(RpfFileEntry entry)
+ {
+ FileEntry = entry;
+ }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ FileEntry = entry;
+ Name = entry.Name;
+
+ MemoryStream ms = new MemoryStream(data);
+ BinaryReader br = new BinaryReader(ms);
+ StringBuilder sb = new StringBuilder();
+
+ Type = br.ReadUInt32(); //type/version?
+
+ DataLength = br.ReadUInt32(); //length of data block
+ DataBlock = br.ReadBytes((int)DataLength); //main data block...
+
+ NameTableLength = br.ReadUInt32(); //length of this nametable block
+ NameTableCount = br.ReadUInt32();
+ if (NameTableCount > 0)
+ {
+ uint[] d02 = new uint[NameTableCount]; //string offsets
+ for (uint i = 0; i < NameTableCount; i++)
+ {
+ d02[i] = br.ReadUInt32();
+ }
+ NameTableOffsets = d02;
+ string[] names = new string[NameTableCount];
+ for (uint i = 0; i < NameTableCount; i++)
+ {
+ sb.Clear();
+ while (true)
+ {
+ char c = (char)br.ReadByte();
+ if (c != 0) sb.Append(c);
+ else break;
+ }
+ names[i] = sb.ToString();
+
+ //JenkIndex.Ensure(names[i]); //really need both here..?
+ JenkIndex.Ensure(names[i].ToLowerInvariant());
+ }
+ NameTable = names;
+ }
+
+ IndexCount = br.ReadUInt32(); //count of index items
+ if (IndexCount > 0)
+ {
+ //checking NameTableLength here doesn't make sense!
+ if ((Type == 4) && (NameTableLength == 4))//audioconfig.dat4.rel
+ {
+ IndexStringFlags = br.ReadUInt32(); //what is this? 2524
+ RelIndexString[] indexstrs = new RelIndexString[IndexCount];
+ for (uint i = 0; i < IndexCount; i++)
+ {
+ byte sl = br.ReadByte();
+ sb.Clear();
+ for (int j = 0; j < sl; j++)
+ {
+ char c = (char)br.ReadByte();
+ if (c != 0) sb.Append(c);
+ }
+ RelIndexString cunk01 = new RelIndexString();
+ cunk01.Name = sb.ToString();
+ cunk01.Offset = br.ReadUInt32();
+ cunk01.Length = br.ReadUInt32();
+ indexstrs[i] = cunk01;
+ }
+ IndexStrings = indexstrs;
+ }
+ else //for all other .rel files...
+ {
+ RelIndexHash[] indexhashes = new RelIndexHash[IndexCount];
+ for (uint i = 0; i < IndexCount; i++)
+ {
+ RelIndexHash unk01 = new RelIndexHash();
+ unk01.Name = new MetaHash(br.ReadUInt32());
+ unk01.Offset = br.ReadUInt32();
+ unk01.Length = br.ReadUInt32();
+ indexhashes[i] = unk01;
+ }
+ IndexHashes = indexhashes;
+ }
+ }
+
+
+ Unk05Count = br.ReadUInt32();
+ if (Unk05Count != 0)
+ {
+ uint[] d05 = new uint[Unk05Count];
+ MetaHash[] d05h = new MetaHash[Unk05Count];
+ for (uint i = 0; i < Unk05Count; i++)
+ {
+ d05[i] = br.ReadUInt32();
+
+ var pos = ms.Position;
+ ms.Position = d05[i];
+ d05h[i] = new MetaHash(br.ReadUInt32());
+ ms.Position = pos;
+ }
+ Unk05Arr = d05;
+ Unk05Hashes = d05h;
+ }
+
+ ContainerCount = br.ReadUInt32();
+ if (ContainerCount != 0)
+ {
+ uint[] cunks = new uint[ContainerCount];
+ MetaHash[] chashes = new MetaHash[ContainerCount];
+ for (uint i = 0; i < ContainerCount; i++)
+ {
+ cunks[i] = br.ReadUInt32();
+
+ var pos = ms.Position;
+ ms.Position = cunks[i];
+ chashes[i] = new MetaHash(br.ReadUInt32());
+ ms.Position = pos;
+ }
+ ContainerUnkArr = cunks;
+ ContainerHashes = chashes;
+ }
+
+ if (ms.Position != ms.Length)
+ { }
+ //EOF!
+
+ br.Dispose();
+ ms.Dispose();
+
+
+ ParseDataBlock();
+ }
+
+
+ private void ParseDataBlock()
+ {
+
+
+
+ MemoryStream ms = new MemoryStream(DataBlock);
+ BinaryReader br = new BinaryReader(ms);
+
+ DataUnkVal = br.ReadUInt32(); //3 bytes used... for? ..version? flags?
+ //switch (DataUnkVal)
+ //{
+ // case 5252715: //dlcbusiness_amp.dat10.rel
+ // case 5301323: //dlcbeach_game.dat149.rel
+ // case 5378673: //dlcmpheist_game.dat150.rel
+ // case 5750395: //dlcbeach_game.dat150.rel
+ // case 6353778: //dlcbeach_game.dat151.rel
+ // case 6894089: //dlcpilotschool_game.dat151.rel
+ // case 6978435: //dlcxmas2_amp.dat10.rel
+ // case 7126027: //audioconfig.dat4.rel
+ // case 7314721: //dlcmpheist_amp.dat10.rel
+ // case 7516460: //dlcpd03_game.dat151.rel
+ // case 7917027: //dlcluxe_amp.dat10.rel
+ // case 7921508: //dlcluxe_game.dat151.rel
+ // case 8149475: //dlcluxe2_amp.dat10.rel
+ // case 8751734: //dlcsfx1_game.dat151.rel
+ // case 9028036: //dlchalloween_amp.dat10.rel
+ // case 9037528: //dlclowrider_amp.dat10.rel
+ // case 9458585: //dlcapartment_amp.dat10.rel
+ // case 9486222: //dlcapartment_mix.dat15.rel
+ // case 9806108: //mpvalentines2_amp.dat10.rel
+ // case 9813679: //dlcjanuary2016_amp.dat10.rel
+ // case 10269543://dlclow2_amp.dat10.rel
+ // case 10891463://dlcexec1_amp.dat10.rel
+ // case 11171338://dlcstunt_amp.dat10.rel
+ // case 11918985://dlcbiker_amp.dat10.rel
+ // case 12470522://dlcimportexport_amp.dat10.rel
+ // case 12974726://audioconfig.dat4.rel
+ // case 13117164://dlcspecialraces_amp.dat10.rel
+ // break;
+ // default:
+ // break;
+ //}
+
+
+ List reldatas = new List();
+ if (IndexHashes != null)
+ {
+ foreach (var indexhash in IndexHashes)
+ {
+ reldatas.Add(ReadRelData(br, indexhash));
+ }
+ }
+ else if (IndexStrings != null)
+ {
+ foreach (var indexstr in IndexStrings)
+ {
+ reldatas.Add(ReadRelData(br, indexstr));
+ }
+ }
+ RelDatas = reldatas.ToArray();
+
+ reldatas.Sort((d1, d2) => d1.DataOffset.CompareTo(d2.DataOffset));
+ RelDatasSorted = reldatas.ToArray();
+
+
+ br.Dispose();
+ ms.Dispose();
+
+
+
+
+ RelDataDict.Clear();
+ foreach (var reldata in RelDatas)
+ {
+ if ((reldata.NameHash == 0) && !string.IsNullOrEmpty(reldata.Name))
+ {
+ reldata.NameHash = JenkHash.GenHash(reldata.Name); //should this be lower case?
+ JenkIndex.Ensure(reldata.Name);
+ JenkIndex.Ensure(reldata.Name.ToLowerInvariant()); //which one to use?
+ }
+
+ //if (reldata.NameHash == 0)
+ //{ }//no hits here
+ //if (RelDataDict.ContainsKey(reldata.NameHash))
+ //{ }//no hits here
+
+ RelDataDict[reldata.NameHash] = reldata;
+ }
+ foreach (var reldata in RelDatas)
+ {
+ RelSound snd = reldata as RelSound;
+ if (snd != null)
+ {
+ if (snd.AudioTracksCount > 0)
+ {
+ snd.AudioTracks = new RelData[snd.AudioTracksCount];
+ for (int i = 0; i < snd.AudioTracksCount; i++)
+ {
+ var audhash = snd.AudioTrackHashes[i];
+ RelData auddata = null;
+ if (RelDataDict.TryGetValue(audhash, out auddata))
+ {
+ snd.AudioTracks[i] = auddata;
+ }
+ else
+ { }
+ }
+ }
+ if (snd.AudioContainers != null)
+ {
+ foreach (var cnt in snd.AudioContainers)
+ {
+ string cname = JenkIndex.TryGetString(cnt.Hash);
+ if (!string.IsNullOrEmpty(cname))
+ { }
+ else
+ { }
+ }
+ }
+ }
+ }
+
+ #region test
+ //foreach (var d in RelDatas)
+ //{
+ // using (BinaryReader dbr = new BinaryReader(new MemoryStream(d.Data)))
+ // {
+ // switch (Type)
+ // {
+ // case 4: //00000100 //speech.dat4.rel, audioconfig.dat4.rel
+ // ParseData4(d, dbr);
+ // break;
+ // case 10: //00001010 //amp.dat10.rel
+ // ParseData10(d, dbr);
+ // break;
+ // case 15: //00001111 //mix.dat15.rel
+ // ParseData15(d, dbr);
+ // break;
+ // case 16: //00010000 //curves.dat16.rel
+ // ParseData16(d, dbr);
+ // break;
+ // case 22: //00010110 //categories.dat22.rel
+ // ParseData22(d, dbr);
+ // break;
+ // case 54: //00110110 //sounds.dat54.rel
+ // ParseData54(d, dbr);
+ // break;
+ // case 149: //10010101 //game.dat149.rel
+ // ParseData149(d, dbr);
+ // break;
+ // case 150: //10010110 //game.dat150.rel
+ // ParseData150(d, dbr);
+ // break;
+ // case 151: //10010111 //game.dat151.rel
+ // ParseData151(d, dbr);
+ // break;
+ // default:
+ // break;
+ // }
+ // }
+ //}
+ #endregion
+
+ }
+
+
+
+
+ private RelData ReadRelData(BinaryReader br, RelIndexHash h)
+ {
+ return ReadRelData(br, null, h.Name, h.Offset, h.Length);
+ }
+ private RelData ReadRelData(BinaryReader br, RelIndexString s)
+ {
+ return ReadRelData(br, s.Name, 0, s.Offset, s.Length);
+ }
+ private RelData ReadRelData(BinaryReader br, string name, MetaHash hash, uint offset, uint length)
+ {
+ br.BaseStream.Position = offset;
+ byte[] data = br.ReadBytes((int)length);
+
+
+ RelData d = new RelData(); //use this base object to construct the derived one...
+ d.Name = name;
+ d.NameHash = hash;
+ d.DataOffset = offset;
+ d.DataLength = length;
+ d.Data = data;
+
+
+ using (BinaryReader dbr = new BinaryReader(new MemoryStream(data)))
+ {
+ d.ReadType(dbr);
+
+ switch (Type)
+ {
+ case 4: //speech.dat4.rel, audioconfig.dat4.rel
+ return ReadData4(d, dbr);
+ case 10: //amp.dat10.rel
+ return ReadData10(d, dbr);
+ case 15: //mix.dat15.rel
+ return ReadData15(d, dbr);
+ case 16: //curves.dat16.rel
+ return ReadData16(d, dbr);
+ case 22: //categories.dat22.rel
+ return ReadData22(d, dbr);
+ case 54: //sounds.dat54.rel
+ return ReadData54(d, dbr);
+ case 149: //game.dat149.rel
+ return ReadData149(d, dbr);
+ case 150: //game.dat150.rel
+ return ReadData150(d, dbr);
+ case 151: //game.dat151.rel
+ return ReadData151(d, dbr);
+ default:
+ return d; //shouldn't get here...
+ }
+ }
+ }
+
+
+
+ private RelData ReadData4(RelData d, BinaryReader br)
+ {
+ if (NameTableLength == 4) //(for audioconfig.dat4.rel)
+ {
+ }
+ else //(for eg speech.dat4.rel)
+ {
+ }
+ return d;
+ }
+ private RelData ReadData10(RelData d, BinaryReader br)
+ {
+ return d;
+ }
+ private RelData ReadData15(RelData d, BinaryReader br)
+ {
+ return d;
+ }
+ private RelData ReadData16(RelData d, BinaryReader br)
+ {
+ return d;
+ }
+ private RelData ReadData22(RelData d, BinaryReader br)
+ {
+ //RelSound s = new RelSound(d, br);
+ //return s;
+ return d;
+ }
+ private RelData ReadData54(RelData d, BinaryReader br)
+ {
+ switch ((Dat54SoundType)d.TypeID)
+ {
+ case Dat54SoundType.LoopingSound: return new Dat54LoopingSound(d, br);
+ case Dat54SoundType.EnvelopeSound: return new Dat54EnvelopeSound(d, br);
+ case Dat54SoundType.TwinLoopSound: return new Dat54TwinLoopSound(d, br);
+ case Dat54SoundType.SpeechSound: return new Dat54SpeechSound(d, br);
+ case Dat54SoundType.OnStopSound: return new Dat54OnStopSound(d, br);
+ case Dat54SoundType.WrapperSound: return new Dat54WrapperSound(d, br);
+ case Dat54SoundType.SequentialSound: return new Dat54SequentialSound(d, br);
+ case Dat54SoundType.StreamingSound: return new Dat54StreamingSound(d, br);
+ case Dat54SoundType.RetriggeredOverlappedSound: return new Dat54RetriggeredOverlappedSound(d, br);
+ case Dat54SoundType.CrossfadeSound: return new Dat54CrossfadeSound(d, br);
+ case Dat54SoundType.CollapsingStereoSound: return new Dat54CollapsingStereoSound(d, br);
+ case Dat54SoundType.SimpleSound: return new Dat54SimpleSound(d, br);
+ case Dat54SoundType.MultitrackSound: return new Dat54MultitrackSound(d, br);
+ case Dat54SoundType.RandomizedSound: return new Dat54RandomizedSound(d, br);
+ case Dat54SoundType.EnvironmentSound: return new Dat54EnvironmentSound(d, br);
+ case Dat54SoundType.DynamicEntitySound: return new Dat54DynamicEntitySound(d, br);
+ case Dat54SoundType.SequentialOverlapSound: return new Dat54SequentialOverlapSound(d, br);
+ case Dat54SoundType.ModularSynthSound: return new Dat54ModularSynthSound(d, br);
+ case Dat54SoundType.GranularSound: return new Dat54GranularSound(d, br);
+ case Dat54SoundType.DirectionalSound: return new Dat54DirectionalSound(d, br);
+ case Dat54SoundType.KineticSound: return new Dat54KineticSound(d, br);
+ case Dat54SoundType.SwitchSound: return new Dat54SwitchSound(d, br);
+ case Dat54SoundType.VariableCurveSound: return new Dat54VariableCurveSound(d, br);
+ case Dat54SoundType.VariablePrintValueSound: return new Dat54VariablePrintValueSound(d, br);
+ case Dat54SoundType.VariableBlockSound: return new Dat54VariableBlockSound(d, br);
+ case Dat54SoundType.IfSound: return new Dat54IfSound(d, br);
+ case Dat54SoundType.MathOperationSound: return new Dat54MathOperationSound(d, br);
+ case Dat54SoundType.ParameterTransformSound: return new Dat54ParameterTransformSound(d, br);
+ case Dat54SoundType.FluctuatorSound: return new Dat54FluctuatorSound(d, br);
+ case Dat54SoundType.AutomationSound: return new Dat54AutomationSound(d, br);
+ case Dat54SoundType.ExternalStreamSound: return new Dat54ExternalStreamSound(d, br);
+ case Dat54SoundType.SoundSet: return new Dat54SoundSet(d, br);
+ case Dat54SoundType.Unknown: return new Dat54UnknownSound(d, br);
+ case Dat54SoundType.Unknown2: return new Dat54UnknownSound2(d, br);
+ case Dat54SoundType.SoundList: return new Dat54SoundList(d, br);
+ default:
+ return new Dat54Sound(d, br); //shouldn't get here
+ }
+ }
+ private RelData ReadData149(RelData d, BinaryReader br)
+ {
+ //RelSound s = new RelSound(d, br);
+ //return s;
+ return d;
+ }
+ private RelData ReadData150(RelData d, BinaryReader br)
+ {
+ return d;
+ }
+ private RelData ReadData151(RelData d, BinaryReader br)
+ {
+ switch ((Dat151RelType)d.TypeID)
+ {
+ case Dat151RelType.Collision: //maybe for vehicle
+ case Dat151RelType.Vehicle:
+ case Dat151RelType.VehicleEngine:
+ case Dat151RelType.Entity: //not sure about this
+ case Dat151RelType.Stream: //generic audio stream?
+ case Dat151RelType.Helicopter: //maybe
+ case Dat151RelType.SpeechParams:
+ case Dat151RelType.Weapon:
+ case Dat151RelType.RadioStationsDLC: //
+ case Dat151RelType.RadioDLC:
+ case Dat151RelType.DLCMusic:
+ case Dat151RelType.PedPVG: //maybe Ped Voice Group?
+ case Dat151RelType.WeaponAudioItem:
+ case Dat151RelType.Aeroplane:
+ case Dat151RelType.Mood:
+ case Dat151RelType.StartTrackAction:
+ case Dat151RelType.StopTrackAction:
+ case Dat151RelType.SetMoodAction:
+ case Dat151RelType.PlayerAction:
+ case Dat151RelType.StartOneShotAction:
+ case Dat151RelType.StopOneShotAction:
+ case Dat151RelType.AnimalParams:
+ case Dat151RelType.VehicleScannerParams: //maybe not just vehicle
+ case Dat151RelType.Explosion:
+ case Dat151RelType.VehicleEngineGranular: //maybe not just vehicle
+ case Dat151RelType.ShoreLinePool:
+ case Dat151RelType.ShoreLineLake:
+ case Dat151RelType.ShoreLineRiver:
+ case Dat151RelType.ShoreLineOcean:
+ case Dat151RelType.ShoreLineList:
+ case Dat151RelType.RadioDjSpeechAction:
+ case Dat151RelType.FadeOutRadioAction:
+ case Dat151RelType.FadeInRadioAction:
+ case Dat151RelType.ForceRadioTrackAction:
+ case Dat151RelType.Unk2:
+ case Dat151RelType.Unk7:
+ case Dat151RelType.Unk9:
+ case Dat151RelType.Unk11:
+ case Dat151RelType.Unk12:
+ case Dat151RelType.Unk13:
+ case Dat151RelType.Unk15:
+ case Dat151RelType.Unk16:
+ case Dat151RelType.Unk18:
+ case Dat151RelType.Unk22:
+ case Dat151RelType.Unk23:
+ case Dat151RelType.Unk27:
+ case Dat151RelType.Unk28:
+ case Dat151RelType.Unk29:
+ case Dat151RelType.Unk31:
+ case Dat151RelType.Unk33:
+ case Dat151RelType.Unk35:
+ case Dat151RelType.Unk36:
+ case Dat151RelType.Unk40:
+ case Dat151RelType.Unk41:
+ case Dat151RelType.Unk42:
+ case Dat151RelType.Interior:
+ case Dat151RelType.Unk45:
+ case Dat151RelType.InteriorRoom:
+ case Dat151RelType.Unk47:
+ case Dat151RelType.Unk48:
+ case Dat151RelType.Unk49:
+ case Dat151RelType.Unk51:
+ case Dat151RelType.Mod:
+ case Dat151RelType.Unk53:
+ case Dat151RelType.Unk54:
+ case Dat151RelType.Unk55:
+ case Dat151RelType.Unk56:
+ case Dat151RelType.Unk59:
+ case Dat151RelType.Unk69:
+ case Dat151RelType.Unk70:
+ case Dat151RelType.Unk71:
+ case Dat151RelType.Unk72:
+ case Dat151RelType.Unk74:
+ case Dat151RelType.Unk75:
+ case Dat151RelType.Unk77:
+ case Dat151RelType.Unk78:
+ case Dat151RelType.Unk79:
+ case Dat151RelType.Unk80:
+ case Dat151RelType.Unk81:
+ case Dat151RelType.Unk82:
+ case Dat151RelType.Unk83:
+ case Dat151RelType.Unk84:
+ case Dat151RelType.Unk85:
+ case Dat151RelType.Unk86:
+ case Dat151RelType.Unk95:
+ case Dat151RelType.Unk96:
+ case Dat151RelType.Unk99:
+ case Dat151RelType.Unk100:
+ case Dat151RelType.Unk101:
+ case Dat151RelType.Unk105:
+ case Dat151RelType.Unk106:
+ case Dat151RelType.Unk107:
+ case Dat151RelType.Unk108:
+ case Dat151RelType.Unk109:
+ case Dat151RelType.Unk110:
+ case Dat151RelType.Unk111:
+ case Dat151RelType.Unk112:
+ case Dat151RelType.Unk113:
+ case Dat151RelType.Unk114:
+ case Dat151RelType.Unk115:
+ case Dat151RelType.Unk116:
+ case Dat151RelType.Unk117:
+ case Dat151RelType.Unk118:
+ case Dat151RelType.Unk119:
+ case Dat151RelType.Unk120:
+ case Dat151RelType.Unk121:
+ return new Dat151RelData(d, br);
+
+ case Dat151RelType.AmbientEmitterList: return new Dat151AmbientEmitterList(d, br);
+ case Dat151RelType.AmbientZone: return new Dat151AmbientZone(d, br);
+ case Dat151RelType.AmbientEmitter: return new Dat151AmbientEmitter(d, br);
+ case Dat151RelType.AmbientZoneList: return new Dat151AmbientZoneList(d, br);
+ default:
+ return new Dat151RelData(d, br);
+ }
+ }
+
+
+
+
+
+ #region first research
+
+ private void ParseData4(RelData d, BinaryReader br)
+ {
+ //speech.dat4.rel, audioconfig.dat4.rel
+
+ if (d.DataLength == 1)
+ {
+ byte b = br.ReadByte();
+ switch (b)
+ {
+ case 0:
+ case 25:
+ case 28:
+ case 34:
+ case 89:
+ case 94:
+ case 178:
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+ if (d.DataLength == 2)
+ {
+ byte b = br.ReadByte();
+ switch (b)
+ {
+ case 4:
+ case 1:
+ case 15:
+ case 12:
+ case 3:
+ case 2:
+ case 7:
+ case 5:
+ case 158:
+ case 25:
+ case 16:
+ case 64:
+ case 6:
+ case 8:
+ case 14:
+ case 22:
+ case 18:
+ case 20:
+ case 32:
+ case 17:
+ case 30:
+ case 9:
+ case 0:
+ case 47:
+ case 224:
+ case 200:
+ case 136:
+ case 45:
+ case 54:
+ case 28:
+ case 19:
+ case 37:
+ case 61:
+ case 38:
+ case 128:
+ case 24:
+ case 26:
+ case 40:
+ case 13:
+ case 36:
+ case 78:
+ case 34:
+ case 10:
+ case 21:
+ case 192:
+ case 60:
+ case 29:
+ case 33:
+ case 72:
+ case 57:
+ case 133:
+ case 11:
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+ if (d.DataLength == 4)
+ {
+ uint h = br.ReadUInt32();
+ return;
+ }
+
+
+ byte b00 = br.ReadByte();
+ switch (b00)
+ {
+ case 4:
+ case 1:
+ case 0:
+ case 6:
+ case 3:
+ case 2:
+ case 5:
+ case 7:
+ case 15:
+ case 10:
+ case 8:
+ case 9:
+ break;
+
+ case 23:
+ case 12:
+ case 11:
+ case 16:
+ case 13:
+ case 36:
+ case 30:
+ case 31:
+ case 27:
+ case 20:
+ case 19:
+ case 14:
+ case 40:
+ case 46:
+ case 22:
+ case 18:
+ case 21:
+ case 45:
+ case 17:
+ case 48:
+ case 87:
+ case 38:
+ case 28:
+ case 29:
+ case 43:
+ case 69:
+ case 50:
+ case 25:
+ case 32:
+ case 35:
+ case 34:
+ break;
+ default:
+ break;
+ }
+ }
+ private void ParseData10(RelData d, BinaryReader br)
+ {
+ //amp.dat10.rel
+
+ byte b00 = br.ReadByte();
+ switch (b00)
+ {
+ case 1:
+ case 3:
+ break;
+ default:
+ break;
+ }
+ }
+ private void ParseData15(RelData d, BinaryReader br)
+ {
+ //mix.dat15.rel
+
+ byte b00 = br.ReadByte();
+ switch (b00)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ break;
+ default:
+ break;
+ }
+ }
+ private void ParseData16(RelData d, BinaryReader br)
+ {
+ //curves.dat16.rel
+
+ byte b00 = br.ReadByte();
+ switch (b00)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 12:
+ case 13:
+ case 15:
+ break;
+ default:
+ break;
+ }
+ }
+ private void ParseData22(RelData d, BinaryReader br)
+ {
+ //categories.dat22.rel
+
+ byte b00 = br.ReadByte();
+ switch (b00)
+ {
+ case 0:
+ break;
+ default:
+ break;
+ }
+ }
+ private void ParseData54(RelData d, BinaryReader br)
+ {
+ //sounds.dat54.rel
+
+ byte b00 = br.ReadByte();
+ switch (b00)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ break;
+ default:
+ break;
+ }
+ }
+ private void ParseData149(RelData d, BinaryReader br)
+ {
+ //game.dat149.rel
+
+ byte b00 = br.ReadByte();
+ switch (b00)
+ {
+ case 3:
+ case 4:
+ case 17:
+ case 50:
+ case 57:
+ case 62:
+ case 63:
+ case 66:
+ case 76:
+ case 88:
+ case 90:
+ break;
+ default:
+ break;
+ }
+ }
+ private void ParseData150(RelData d, BinaryReader br)
+ {
+ //game.dat150.rel
+
+ byte b00 = br.ReadByte();
+ switch (b00)
+ {
+ case 3:
+ case 4:
+ case 6:
+ case 8:
+ case 17:
+ case 32:
+ case 37:
+ case 38:
+ case 39:
+ case 47:
+ case 50:
+ case 52:
+ case 57:
+ case 62:
+ case 63:
+ case 64:
+ case 65:
+ case 66:
+ case 76:
+ case 88:
+ case 90:
+ case 117:
+ break;
+ default:
+ break;
+ }
+ }
+ private void ParseData151(RelData d, BinaryReader br)
+ {
+ //game.dat151.rel
+
+ byte b00 = br.ReadByte(); //???
+ switch (b00)
+ {
+ case 1://new
+ case 2://new
+ case 3:
+ case 4:
+ case 5://new
+ case 6:
+ case 7://new
+ case 8://
+ case 9://new
+ case 11://new
+ case 12://new
+ case 13://new
+ case 14://new
+ case 15://new
+ case 16://new
+ case 17:
+ case 18://new
+ case 22://new
+ case 23://new
+ case 24://new
+ case 25://new
+ case 26://new
+ case 27://new
+ case 28://new
+ case 29://new
+ case 30://new
+ case 31://new
+ case 32://
+ case 33://new
+ case 35://new
+ case 36://new
+ case 37://
+ case 38://
+ case 39://
+ case 40://new
+ case 41://new
+ case 42://new
+ case 44://new
+ case 45://new
+ case 46://new
+ case 47://
+ case 48://new
+ case 49://new
+ case 50:
+ case 51://new
+ case 52://
+ case 53://new
+ case 54://new
+ case 55://new
+ case 56://new
+ case 57:
+ case 59://new
+ case 62:
+ case 63:
+ case 64:
+ case 65://
+ case 66:
+ case 67://new
+ case 68://new
+ case 69://new
+ case 70://new
+ case 71://new
+ case 72://new
+ case 73://new
+ case 74://new
+ case 75://new
+ case 76:
+ case 77://new
+ case 78://new
+ case 79://new
+ case 80://new
+ case 81://new
+ case 82://new
+ case 83://new
+ case 84://new
+ case 85://new
+ case 86://new
+ case 87://new
+ case 88:
+ case 90:
+ case 91://new
+ case 92://new
+ case 93://new
+ case 94://new
+ case 95://new
+ case 96://new
+ case 98://new
+ case 99://new
+ case 100://new
+ case 101://new
+ case 102://new
+ case 103://new
+ case 104://new
+ case 105://new
+ case 106://new
+ case 107://new
+ case 108://new
+ case 109://new
+ case 110://new
+ case 111://new
+ case 112://new
+ case 113://new
+ case 114://new
+ case 115://new
+ case 116://new
+ case 117:
+ case 118://new
+ case 119://new
+ case 120://new
+ case 121://new
+ break;
+ default:
+ break;
+ }
+ }
+
+ #endregion
+
+
+
+ public override string ToString()
+ {
+ return Name;
+ }
+ }
+
+ [TC(typeof(EXP))] public struct RelIndexHash
+ {
+ public MetaHash Name { get; set; }
+ public uint Offset { get; set; }
+ public uint Length { get; set; }
+
+ public override string ToString()
+ {
+ return Name.ToString() + ", " + Offset.ToString() + ", " + Length.ToString();
+ }
+ }
+
+
+ [TC(typeof(EXP))] public struct RelIndexString
+ {
+ public string Name { get; set; }
+ public uint Offset { get; set; }
+ public uint Length { get; set; }
+
+ public override string ToString()
+ {
+ return Name + ", " + Offset.ToString() + ", " + Length.ToString();
+ }
+ }
+
+
+
+ [TC(typeof(EXP))] public class RelData
+ {
+ public MetaHash NameHash { get; set; }
+ public string Name { get; set; }
+ public uint DataOffset { get; set; }
+ public uint DataLength { get; set; }
+ public byte[] Data { get; set; }
+ public byte TypeID { get; set; }
+
+ public RelData() { }
+ public RelData(RelData d)
+ {
+ NameHash = d.NameHash;
+ Name = d.Name;
+ DataOffset = d.DataOffset;
+ DataLength = d.DataLength;
+ Data = d.Data;
+ TypeID = d.TypeID;
+ }
+
+ public void ReadType(BinaryReader br)
+ {
+ TypeID = br.ReadByte();
+ }
+
+ public string GetNameString()
+ {
+ return (string.IsNullOrEmpty(Name)) ? NameHash.ToString() : Name;
+ }
+ public string GetBaseString()
+ {
+ return DataOffset.ToString() + ", " + DataLength.ToString() + ": " + GetNameString();
+ }
+ public override string ToString()
+ {
+ return GetBaseString() + ": " + TypeID.ToString();
+ }
+
+ public static bool Bit(uint f, int b)
+ {
+ return ((f & (1u << b)) != 0); //just for handyness... maybe move this?
+ }
+ public static bool BadF(float f)
+ {
+ return ((f < -15000) || (f > 15000));
+ }
+ }
+
+ [TC(typeof(EXP))] public class RelSoundHeader
+ {
+ public FlagsUint Flags { get; set; }
+
+ public FlagsUint UnkFlags { get; set; }
+ public ushort Unk01 { get; set; }
+ public ushort Unk02 { get; set; }
+ public ushort Unk03 { get; set; } //0xD-0xF
+ public ushort Unk04 { get; set; } //0xF-0x11
+ public ushort Unk05 { get; set; } //0x11-0x13
+ public ushort Unk06 { get; set; } //0x13-0x15
+ public ushort Unk07 { get; set; } //0x15-0x17
+ public ushort Unk08 { get; set; } //0x17-0x19
+ public ushort Unk09 { get; set; } //0x19-0x1B
+ public MetaHash UnkHash1 { get; set; } //0x1B-0x1F
+ public MetaHash UnkHash2 { get; set; } //0x1F-0x23
+ public ushort Unk10 { get; set; } //0x23-0x25
+ public ushort Unk11 { get; set; } //0x25-0x27
+ public ushort Unk12 { get; set; } //0x27-0x29
+ public MetaHash CategoryHash { get; set; } //0x29-0x2D
+ public ushort Unk14 { get; set; } //0x2D-0x2F
+ public ushort Unk15 { get; set; } //0x2F-0x31
+ public ushort Unk16 { get; set; } //0x31-0x33
+ public ushort Unk17 { get; set; } //0x33-0x35
+ public MetaHash UnkHash3 { get; set; } //0x35-0x39
+ public ushort Unk18 { get; set; } //0x39-0x3B
+ public byte Unk19 { get; set; } //0x3B-0x3C
+ public byte Unk20 { get; set; } //0x3C-0x3D
+ public byte Unk21 { get; set; } //0x3D-0x3E
+ public MetaHash UnkHash4 { get; set; } //0x3E-0x42
+ public MetaHash UnkHash5 { get; set; } //0x42-0x46
+ public ushort Unk22 { get; set; } //0x46-0x48
+ public ushort Unk23 { get; set; } //0x48-0x4A
+ public ushort Unk24 { get; set; } //0x4A-0x4C
+
+ public ushort Unk25 { get; set; } //0x4A-0x4C
+ public ushort Unk26 { get; set; } //0x4A-0x4C
+
+
+ public RelSoundHeader(BinaryReader br)
+ {
+ Flags = br.ReadUInt32();
+
+
+ //if (Flags.Value != 0xAAAAAAAA)
+ if ((Flags.Value & 0xFF) != 0xAA)
+ {
+ if (Bit(0)) UnkFlags = br.ReadUInt32();
+ if (Bit(1)) Unk01 = br.ReadUInt16();
+ if (Bit(2)) Unk02 = br.ReadUInt16();
+ if (Bit(3)) Unk03 = br.ReadUInt16();
+ if (Bit(4)) Unk04 = br.ReadUInt16();
+ if (Bit(5)) Unk05 = br.ReadUInt16();
+ if (Bit(6)) Unk06 = br.ReadUInt16();
+ if (Bit(7)) Unk07 = br.ReadUInt16();
+ }
+ if ((Flags.Value & 0xFF00) != 0xAA00)
+ {
+ if (Bit(8)) Unk08 = br.ReadUInt16();
+ if (Bit(9)) Unk09 = br.ReadUInt16();
+ if (Bit(10)) UnkHash1 = br.ReadUInt32();
+ if (Bit(11)) UnkHash2 = br.ReadUInt32();
+ if (Bit(12)) Unk10 = br.ReadUInt16();
+ if (Bit(13)) Unk11 = br.ReadUInt16();
+ if (Bit(14)) Unk12 = br.ReadUInt16();
+ if (Bit(15)) CategoryHash = br.ReadUInt32();
+ }
+ if ((Flags.Value & 0xFF0000) != 0xAA0000)
+ {
+ if (Bit(16)) Unk14 = br.ReadUInt16();
+ if (Bit(17)) Unk15 = br.ReadUInt16();
+ if (Bit(18)) Unk16 = br.ReadUInt16();
+ if (Bit(19)) Unk17 = br.ReadUInt16();
+ if (Bit(20)) UnkHash3 = br.ReadUInt32();
+ if (Bit(21)) Unk18 = br.ReadUInt16();
+ if (Bit(22)) Unk19 = br.ReadByte();
+ if (Bit(23)) Unk20 = br.ReadByte();
+ }
+ if ((Flags.Value & 0xFF000000) != 0xAA000000)
+ {
+ if (Bit(24)) Unk21 = br.ReadByte();
+ if (Bit(25)) UnkHash4 = br.ReadUInt32();
+ if (Bit(26)) UnkHash5 = br.ReadUInt32();
+ if (Bit(27)) Unk22 = br.ReadUInt16();
+ if (Bit(28)) Unk23 = br.ReadUInt16();
+ if (Bit(29)) Unk24 = br.ReadUInt16();
+ if (Bit(30)) Unk25 = br.ReadUInt16(); //maybe not
+ if (Bit(31)) Unk26 = br.ReadUInt16(); //maybe not
+ }
+ }
+
+ private bool Bit(int b)
+ {
+ return ((Flags.Value & (1u << b)) != 0);
+ }
+
+ public override string ToString()
+ {
+ return string.Format("{0}: {1}, {2}, {3}, {4}, {5}, {6}, {7}", Flags.Hex, UnkFlags.Hex, CategoryHash, UnkHash1, UnkHash2, UnkHash3, UnkHash4, UnkHash5);
+ }
+ }
+
+ [TC(typeof(EXP))] public class RelSound : RelData
+ {
+ public RelSoundHeader Header { get; set; }
+ public byte AudioTracksCount { get; set; }
+ public RelData[] AudioTracks { get; set; }
+ public MetaHash[] AudioTrackHashes { get; set; }
+ public MetaHash[] AudioContainers { get; set; } //Relative path to parent wave container (i.e. "RESIDENT/animals")
+
+ public RelSound(RelData d, BinaryReader br) : base(d)
+ {
+ Header = new RelSoundHeader(br);
+ }
+
+ public void ReadAudioTrackHashes(BinaryReader br)
+ {
+ AudioTracksCount = br.ReadByte();
+ AudioTrackHashes = new MetaHash[AudioTracksCount];
+ for (int i = 0; i < AudioTracksCount; i++)
+ {
+ AudioTrackHashes[i] = br.ReadUInt32();
+ }
+ }
+ }
+
+
+
+ public enum Dat54SoundType : byte
+ {
+ LoopingSound = 1,
+ EnvelopeSound = 2,
+ TwinLoopSound = 3,
+ SpeechSound = 4,
+ OnStopSound = 5,
+ WrapperSound = 6,
+ SequentialSound = 7,
+ StreamingSound = 8,
+ RetriggeredOverlappedSound = 9,
+ CrossfadeSound = 10,
+ CollapsingStereoSound = 11,
+ SimpleSound = 12,
+ MultitrackSound = 13,
+ RandomizedSound = 14,
+ EnvironmentSound = 15,
+ DynamicEntitySound = 16,
+ SequentialOverlapSound = 17,
+ ModularSynthSound = 18,
+ GranularSound = 19,
+ DirectionalSound = 20,
+ KineticSound = 21,
+ SwitchSound = 22,
+ VariableCurveSound = 23,
+ VariablePrintValueSound = 24,
+ VariableBlockSound = 25,
+ IfSound = 26,
+ MathOperationSound = 27,
+ ParameterTransformSound = 28,
+ FluctuatorSound = 29,
+ AutomationSound = 30,
+ ExternalStreamSound = 31,
+ SoundSet = 32,
+ Unknown = 33,
+ Unknown2 = 34,
+ SoundList = 35
+ }
+
+ [TC(typeof(EXP))] public class Dat54Sound : RelSound
+ {
+ public Dat54SoundType Type { get; set; }
+
+ public Dat54Sound(RelData d, BinaryReader br) : base(d, br)
+ {
+ Type = (Dat54SoundType)TypeID;
+ }
+
+ public override string ToString()
+ {
+ return GetBaseString() + ": " + Type.ToString();
+ }
+ }
+
+ [TC(typeof(EXP))] public class Dat54LoopingSound : Dat54Sound
+ {
+ public short UnkShort0 { get; set; } //0x0-0x2
+ public short UnkShort1 { get; set; } //0x2-0x4
+ public short UnkShort2 { get; set; } //0x4-0x6
+ public MetaHash AudioHash { get; set; } //0x6-0xA
+ public MetaHash ParameterHash { get; set; } //0xA-0xE
+
+ public Dat54LoopingSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkShort0 = br.ReadInt16();
+ UnkShort1 = br.ReadInt16();
+ UnkShort2 = br.ReadInt16();
+ AudioHash = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash };
+ ParameterHash = br.ReadUInt32();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54EnvelopeSound : Dat54Sound
+ {
+ public ushort UnkShortA { get; set; } //0x0-0x2
+ public ushort UnkShortA1 { get; set; } //0x2-0x4
+ public ushort UnkShortB { get; set; } //0x4-0x6
+ public ushort UnkShortB1 { get; set; } //0x6-0x8
+ public byte UnkByteA { get; set; } //0x8-0x9
+ public byte UnkByteA1 { get; set; } //0x9-0xA
+ public int UnkInt { get; set; } //0xA-0xE
+ public ushort UnkShortC { get; set; } //0xE-0x10
+ public int UnkIntA { get; set; } //0x10-0x14
+ public int UnkIntA1 { get; set; } //0x14-0x18
+ public MetaHash CurvesUnkHash0 { get; set; } //0x18-0x1C
+ public MetaHash CurvesUnkHash1 { get; set; } //0x1C-0x20
+ public MetaHash CurvesUnkHash2 { get; set; } //0x20-0x24
+ public MetaHash ParameterHash0 { get; set; } //0x24-0x28
+ public MetaHash ParameterHash1 { get; set; } //0x28-0x2C
+ public MetaHash ParameterHash2 { get; set; } //0x2C-0x30
+ public MetaHash ParameterHash3 { get; set; } //0x30-0x34
+ public MetaHash ParameterHash4 { get; set; } //0x34-0x38
+ public MetaHash AudioHash { get; set; }// audio track 0x38-0x3C
+ public int UnkIntC { get; set; } //0x3C-0x40
+ public MetaHash ParameterHash5 { get; set; } //0x40-0x44
+ public float UnkFloat0 { get; set; } //0x44-0x48
+ public float UnkFloat1 { get; set; } //0x48-0x4C
+
+ public Dat54EnvelopeSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkShortA = br.ReadUInt16(); //0x0-0x2
+ UnkShortA1 = br.ReadUInt16(); //0x2-0x4
+ UnkShortB = br.ReadUInt16(); //0x4-0x6
+ UnkShortB1 = br.ReadUInt16(); //0x6-0x8
+ UnkByteA = br.ReadByte(); //0x8-0x9
+ UnkByteA1 = br.ReadByte(); //0x9-0xA
+ UnkInt = br.ReadInt32(); //0xA-0xE
+ UnkShortC = br.ReadUInt16(); //0xE-0x10
+ UnkIntA = br.ReadInt32(); //0x10-0x14
+ UnkIntA1 = br.ReadInt32(); //0x14-0x18
+ CurvesUnkHash0 = br.ReadUInt32(); //0x18-0x1C
+ CurvesUnkHash1 = br.ReadUInt32(); //0x1C-0x20
+ CurvesUnkHash2 = br.ReadUInt32(); //0x20-0x24
+ ParameterHash0 = br.ReadUInt32(); //0x24-0x28
+ ParameterHash1 = br.ReadUInt32(); //0x28-0x2C
+ ParameterHash2 = br.ReadUInt32(); //0x2C-0x30
+ ParameterHash3 = br.ReadUInt32(); //0x30-0x34
+ ParameterHash4 = br.ReadUInt32(); //0x34-0x38
+ AudioHash = br.ReadUInt32(); //0x38-0x3C
+ UnkIntC = br.ReadInt32(); //0x3C-0x40
+ ParameterHash5 = br.ReadUInt32(); //0x40-0x44
+ UnkFloat0 = br.ReadSingle(); //0x44-0x48
+ UnkFloat1 = br.ReadSingle(); //0x48-0x4C
+ AudioTrackHashes = new[] { AudioHash };
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54TwinLoopSound : Dat54Sound
+ {
+ public ushort UnkShort0 { get; set; } //0x0-0x2
+ public ushort UnkShort1 { get; set; } //0x2-0x4
+ public ushort UnkShort2 { get; set; } //0x4-0x6
+ public ushort UnkShort3 { get; set; } //0x6-0x8
+ public MetaHash UnkHash { get; set; } //0x8-0xC
+ public MetaHash ParameterHash0 { get; set; } //0xC-0x10
+ public MetaHash ParameterHash1 { get; set; } //0x10-0x14
+ public MetaHash ParameterHash2 { get; set; } //0x14-0x18
+ public MetaHash ParameterHash3 { get; set; } //0x18-0x1C
+
+ public Dat54TwinLoopSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkShort0 = br.ReadUInt16();
+ UnkShort1 = br.ReadUInt16();
+ UnkShort2 = br.ReadUInt16();
+ UnkShort3 = br.ReadUInt16();
+ UnkHash = br.ReadUInt32();
+ ParameterHash0 = br.ReadUInt32();
+ ParameterHash1 = br.ReadUInt32();
+ ParameterHash2 = br.ReadUInt32();
+ ParameterHash3 = br.ReadUInt32();
+
+ ReadAudioTrackHashes(br);
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54SpeechSound : Dat54Sound
+ {
+ public int UnkInt0 { get; set; } //maybe file index?
+ public int UnkInt1 { get; set; } //ox4-0x8
+ public MetaHash VoiceDataHash { get; set; } //0x8-0xC
+ public string SpeechName { get; set; } //0xD-...
+
+ public Dat54SpeechSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkInt0 = br.ReadInt32();
+ UnkInt1 = br.ReadInt32();
+ VoiceDataHash = br.ReadUInt32();
+ SpeechName = br.ReadString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54OnStopSound : Dat54Sound
+ {
+ public MetaHash AudioHash0 { get; set; }
+ public MetaHash AudioHash1 { get; set; }
+ public MetaHash AudioHash2 { get; set; }
+
+ public Dat54OnStopSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash0 = br.ReadUInt32();
+ AudioHash1 = br.ReadUInt32();
+ AudioHash2 = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash0, AudioHash1, AudioHash2 };
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54WrapperSound : Dat54Sound
+ {
+ public MetaHash AudioHash0 { get; set; } //0x0-0x4
+ public int FrameStartTime { get; set; } //0x4-0x8 // maybe start delay?
+ public MetaHash AudioHash1 { get; set; } //0x8-0xC
+ public short FrameTimeInterval { get; set; } //0xC-0xE // [camxx:] My guess is that this is related to the time at which a child sound should start playin (or the length of the sound).
+ public byte ItemCount { get; set; }
+ public MetaHash[] Variables { get; set; } //0xF
+ public byte[] UnkByteData { get; set; } // ...
+
+ public Dat54WrapperSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash0 = br.ReadUInt32();
+ FrameStartTime = br.ReadInt32();
+ AudioHash1 = br.ReadUInt32();
+ FrameTimeInterval = br.ReadInt16();
+ ItemCount = br.ReadByte();
+ Variables = new MetaHash[ItemCount];
+ for (int i = 0; i < ItemCount; i++)
+ {
+ Variables[i] = br.ReadUInt32();
+ }
+ UnkByteData = br.ReadBytes(ItemCount);
+
+ AudioTrackHashes = new[] { AudioHash0, AudioHash1 };
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54SequentialSound : Dat54Sound
+ {
+ public Dat54SequentialSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ ReadAudioTrackHashes(br);
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54StreamingSound : Dat54Sound
+ {
+ int UnkInt { get; set; } //0x0-0x4
+
+ public Dat54StreamingSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkInt = br.ReadInt32();
+
+ ReadAudioTrackHashes(br);
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54RetriggeredOverlappedSound : Dat54Sound
+ {
+ public ushort UnkShort0 { get; set; } //0x0-0x2
+ public ushort UnkShort1 { get; set; } //0x2-0x4
+ public ushort UnkShort2 { get; set; } //0x4-0x6
+ public ushort UnkShort3 { get; set; } // 0x6-0x8
+ public MetaHash ParameterHash0 { get; set; } //0x8-0xC
+ public MetaHash ParameterHash1 { get; set; } //0xC-0x10
+ public MetaHash AudioHash0 { get; set; }
+ public MetaHash AudioHash1 { get; set; }
+ public MetaHash AudioHash2 { get; set; }
+
+ public Dat54RetriggeredOverlappedSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkShort0 = br.ReadUInt16();
+ UnkShort1 = br.ReadUInt16();
+ UnkShort2 = br.ReadUInt16();
+ UnkShort3 = br.ReadUInt16();
+ ParameterHash0 = br.ReadUInt32();
+ ParameterHash1 = br.ReadUInt32();
+ AudioHash0 = br.ReadUInt32();
+ AudioHash1 = br.ReadUInt32();
+ AudioHash2 = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash0, AudioHash1, AudioHash2 };
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54CrossfadeSound : Dat54Sound
+ {
+ public MetaHash AudioHash0 { get; set; }
+ public MetaHash AudioHash1 { get; set; }
+ public byte UnkByte { get; set; } //0x8-0x9
+ public float UnkFloat0 { get; set; } //0x9-0xD
+ public float UnkFloat1 { get; set; } //0xD-0x11
+ public int UnkInt2 { get; set; } //0xD-0x15
+ public MetaHash UnkCurvesHash { get; set; } //0x15-0x19
+ public MetaHash ParameterHash0 { get; set; } //0x19-0x1D
+ public MetaHash ParameterHash1 { get; set; } //0x1D-0x21
+ public MetaHash ParameterHash2 { get; set; } //0x21-0x25
+ public MetaHash ParameterHash3 { get; set; } //0x25-0x29
+ public MetaHash ParameterHash4 { get; set; } //0x29-0x2D
+
+ public Dat54CrossfadeSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash0 = br.ReadUInt32();
+ AudioHash1 = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash0, AudioHash1 };
+ UnkByte = br.ReadByte();
+ UnkFloat0 = br.ReadSingle();
+ UnkFloat1 = br.ReadSingle();
+ UnkInt2 = br.ReadInt32();
+ UnkCurvesHash = br.ReadUInt32();
+ ParameterHash0 = br.ReadUInt32();
+ ParameterHash1 = br.ReadUInt32();
+ ParameterHash2 = br.ReadUInt32();
+ ParameterHash3 = br.ReadUInt32();
+ ParameterHash4 = br.ReadUInt32();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54CollapsingStereoSound : Dat54Sound
+ {
+ public MetaHash AudioHash0 { get; set; }
+ public MetaHash AudioHash1 { get; set; }
+ public float UnkFloat0 { get; set; }
+ public float UnkFloat1 { get; set; }
+ public MetaHash ParameterHash0 { get; set; } //0x10-0x14
+ public MetaHash ParameterHash1 { get; set; } //0x14-0x18
+ public MetaHash ParameterHash2 { get; set; } //0x18-0x1C
+ public MetaHash ParameterHash3 { get; set; } //0x1C-0x20
+ public MetaHash ParameterHash4 { get; set; } //0x20-0x24
+ public MetaHash ParameterHash5 { get; set; } //0x28-0x2C
+ public int UnkInt { get; set; } //0x24-0x28
+ public byte UnkByte { get; set; } //0x2c-0x2D
+
+ public Dat54CollapsingStereoSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash0 = br.ReadUInt32();
+ AudioHash1 = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash0, AudioHash1 };
+ UnkFloat0 = br.ReadSingle(); //0x8
+ UnkFloat1 = br.ReadSingle(); //0xC
+ ParameterHash0 = br.ReadUInt32(); //0x10
+ ParameterHash1 = br.ReadUInt32(); //0x14
+ ParameterHash2 = br.ReadUInt32(); //0x18
+ ParameterHash3 = br.ReadUInt32(); //0x1C
+ ParameterHash4 = br.ReadUInt32(); //0x20
+ UnkInt = br.ReadInt32(); //0x24-0x28
+ ParameterHash5 = br.ReadUInt32(); //0x28-0x2C
+ UnkByte = br.ReadByte(); //0x2C-0x2D
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54SimpleSound : Dat54Sound
+ {
+ public MetaHash ContainerName { get; set; } //Relative path to parent wave container (i.e. "RESIDENT/animals")
+ public MetaHash FileName { get; set; } //Name of the .wav file
+ public byte WaveSlotNum { get; set; } //Internal index of wave (.awc) container
+
+ public Dat54SimpleSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ ContainerName = br.ReadUInt32();
+ AudioContainers = new[] { ContainerName };
+ FileName = br.ReadUInt32();
+ WaveSlotNum = br.ReadByte();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54MultitrackSound : Dat54Sound
+ {
+ public Dat54MultitrackSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ ReadAudioTrackHashes(br);
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54RandomizedSound : Dat54Sound
+ {
+ public byte UnkByte { get; set; } //0x0-0x1 something count?
+ public byte UnkBytesCount { get; set; } //0x1-0x2
+ public byte[] UnkBytes { get; set; }
+ public byte ItemCount { get; set; }
+ public float[] AudioTrackUnkFloats { get; set; } //probability..?
+
+ public Dat54RandomizedSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkByte = br.ReadByte();
+ UnkBytesCount = br.ReadByte();
+ UnkBytes = br.ReadBytes(UnkBytesCount);
+ ItemCount = br.ReadByte();
+ AudioTrackHashes = new MetaHash[ItemCount];
+ AudioTrackUnkFloats = new float[ItemCount];
+ for (int i = 0; i < ItemCount; i++)
+ {
+ AudioTrackHashes[i] = br.ReadUInt32();
+ AudioTrackUnkFloats[i] = br.ReadSingle();
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54EnvironmentSound : Dat54Sound
+ {
+ public byte UnkByte { get; set; }
+
+ public Dat54EnvironmentSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkByte = br.ReadByte();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54DynamicEntitySound : Dat54Sound
+ {
+ public byte ItemCount { get; set; }
+ public MetaHash[] UnkHashes { get; set; }
+
+ public Dat54DynamicEntitySound(RelData d, BinaryReader br) : base(d, br)
+ {
+ ItemCount = br.ReadByte();
+ UnkHashes = new MetaHash[ItemCount];
+ for (int i = 0; i < ItemCount; i++)
+ {
+ UnkHashes[i] = br.ReadUInt32();
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54SequentialOverlapSound : Dat54Sound
+ {
+ public ushort UnkShort { get; set; }
+ public MetaHash ParameterHash0 { get; set; } //0x2-0x6
+ public MetaHash ParameterHash1 { get; set; } //0x6-0xA
+
+ public Dat54SequentialOverlapSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkShort = br.ReadUInt16();
+ ParameterHash0 = br.ReadUInt32();
+ ParameterHash1 = br.ReadUInt32();
+
+ ReadAudioTrackHashes(br);
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54ModularSynthSound : Dat54Sound
+ {
+ public MetaHash OptAmpUnkHash { get; set; } //0x0-0x4
+ public MetaHash UnkHash { get; set; } //0x4-0x8
+ public float UnkFloat { get; set; } //0x8-0xC
+ public int UnkInt { get; set; } //0xC-0x10
+ public int TrackCount { get; set; }
+ public int UnkItemCount { get; set; }
+ public Dat54ModularSynthSoundData[] UnkItems { get; set; } //0x28-..
+
+ public Dat54ModularSynthSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ OptAmpUnkHash = br.ReadUInt32(); //0x0-0x4
+ UnkHash = br.ReadUInt32(); //0x4-0x8
+ UnkFloat = br.ReadSingle(); //0x8-0xC
+ UnkInt = br.ReadInt32(); //0xC-0x10
+ TrackCount = br.ReadInt32(); //0x10-0x14
+ AudioTrackHashes = new MetaHash[4];
+ for (int i = 0; i < 4; i++)
+ {
+ AudioTrackHashes[i] = br.ReadUInt32();
+ }
+ UnkItemCount = br.ReadInt32();
+ UnkItems = new Dat54ModularSynthSoundData[UnkItemCount];
+ for (int i = 0; i < UnkItemCount; i++)
+ {
+ UnkItems[i] = new Dat54ModularSynthSoundData(br);
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54ModularSynthSoundData
+ {
+ public MetaHash UnkHash { get; set; }
+ public MetaHash ParameterHash { get; set; }
+ public float Value { get; set; }
+
+ public Dat54ModularSynthSoundData(BinaryReader br)
+ {
+ UnkHash = br.ReadUInt32();
+ ParameterHash = br.ReadUInt32();
+ Value = br.ReadSingle();
+ }
+
+ public override string ToString()
+ {
+ return UnkHash.ToString() + ": " + ParameterHash.ToString() + ": " + FloatUtil.ToString(Value);
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54GranularSound : Dat54Sound
+ {
+ public int WaveSlotIndex { get; set; } //0x0-0x4
+ public Dat54GranularSoundFile Wave1 { get; set; }
+ public Dat54GranularSoundFile Wave2 { get; set; }
+ public Dat54GranularSoundFile Wave3 { get; set; }
+ public Dat54GranularSoundFile Wave4 { get; set; }
+ public Dat54GranularSoundFile Wave5 { get; set; }
+ public Dat54GranularSoundFile Wave6 { get; set; }
+ public Dat54GranularSoundData DataItem1 { get; set; } //0x34-0x3C
+ public Dat54GranularSoundData DataItem2 { get; set; } //0x3C-0x44
+ public Dat54GranularSoundData DataItem3 { get; set; } //0x44-0x4C
+ public Dat54GranularSoundData DataItem4 { get; set; } //0x4C-0x54
+ public Dat54GranularSoundData DataItem5 { get; set; } //0x54-0x5C
+ public Dat54GranularSoundData DataItem6 { get; set; } //0x5C-0x64
+ public int UnkInt0 { get; set; } //0x64-0x68
+ public int UnkInt1 { get; set; } //0x68-0x6C
+ public ushort UnkShort0 { get; set; } //0x6C-0x6E
+ public ushort UnkShort1 { get; set; } //0x6E-0x70
+ public ushort UnkShort2 { get; set; } //0x70-0x72
+ public ushort UnkShort3 { get; set; } //0x72-0x74
+ public ushort UnkShort4 { get; set; } //0x74-0x76
+ public ushort UnkShort5 { get; set; } //0x76-0x78
+ public MetaHash TrackName { get; set; } //0x78-0x7C
+ public byte UnkVecCount { get; set; } //0x7C-0x7D
+ public Vector2[] UnkVecData { get; set; } //0x7D-...
+
+ public Dat54GranularSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ WaveSlotIndex = br.ReadInt32();
+
+ Wave1 = new Dat54GranularSoundFile(br);
+ Wave2 = new Dat54GranularSoundFile(br);
+ Wave3 = new Dat54GranularSoundFile(br);
+ Wave4 = new Dat54GranularSoundFile(br);
+ Wave5 = new Dat54GranularSoundFile(br);
+ Wave6 = new Dat54GranularSoundFile(br);
+
+ AudioContainers = new[] {
+ Wave1.ContainerName,
+ Wave2.ContainerName,
+ Wave3.ContainerName,
+ Wave4.ContainerName,
+ Wave5.ContainerName,
+ Wave6.ContainerName
+ };
+
+ DataItem1 = new Dat54GranularSoundData(br);
+ DataItem2 = new Dat54GranularSoundData(br);
+ DataItem3 = new Dat54GranularSoundData(br);
+ DataItem4 = new Dat54GranularSoundData(br);
+ DataItem5 = new Dat54GranularSoundData(br);
+ DataItem6 = new Dat54GranularSoundData(br);
+
+ UnkInt0 = br.ReadInt32();
+ UnkInt1 = br.ReadInt32();
+ UnkShort0 = br.ReadUInt16();
+ UnkShort1 = br.ReadUInt16();
+ UnkShort2 = br.ReadUInt16();
+ UnkShort3 = br.ReadUInt16();
+ UnkShort4 = br.ReadUInt16();
+ UnkShort5 = br.ReadUInt16();
+
+ TrackName = br.ReadUInt32();
+
+ AudioTrackHashes = new[] { TrackName };
+
+ UnkVecCount = br.ReadByte();
+ UnkVecData = new Vector2[UnkVecCount];
+ for (int i = 0; i < UnkVecCount; i++)
+ {
+ UnkVecData[i] = new Vector2(br.ReadSingle(), br.ReadSingle());
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54GranularSoundFile
+ {
+ public MetaHash ContainerName { get; set; } //0x0-0x4
+ public MetaHash FileName { get; set; } //0x4-0x8
+
+ public Dat54GranularSoundFile(BinaryReader br)
+ {
+ ContainerName = br.ReadUInt32();
+ FileName = br.ReadUInt32();
+ }
+
+ public override string ToString()
+ {
+ return ContainerName.ToString() + ": " + FileName.ToString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54GranularSoundData
+ {
+ public byte UnkFlags0 { get; set; } //0x0-0x1
+ public byte UnkFlags1 { get; set; } //0x1-0x2
+ public byte UnkByte0 { get; set; } //0x2-0x3
+ public byte UnkByte1 { get; set; } //0x3-0x4
+ public float UnkFloat { get; set; } //0x4-0x8
+
+ public Dat54GranularSoundData(BinaryReader br)
+ {
+ UnkFlags0 = br.ReadByte();
+ UnkFlags1 = br.ReadByte();
+ UnkByte0 = br.ReadByte();
+ UnkByte1 = br.ReadByte();
+ UnkFloat = br.ReadSingle();
+ }
+
+ public override string ToString()
+ {
+ return UnkFlags0.ToString() + ": " + UnkFlags1.ToString() + ": " + UnkByte0.ToString() + ": " + UnkByte1.ToString() + ": " + FloatUtil.ToString(UnkFloat);
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54DirectionalSound : Dat54Sound
+ {
+ public MetaHash AudioHash { get; set; }
+ public float UnkFloat0 { get; set; } //0x4-0x8
+ public float UnkFloat1 { get; set; } //0x8-0xC
+ public float UnkFloat2 { get; set; } //0xC-0x10
+ public float UnkFloat3 { get; set; } //0x10-0x14
+ public float UnkFloat4 { get; set; } //0x14-0x18
+
+ public Dat54DirectionalSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash };
+ UnkFloat0 = br.ReadSingle();
+ UnkFloat1 = br.ReadSingle();
+ UnkFloat2 = br.ReadSingle();
+ UnkFloat3 = br.ReadSingle();
+ UnkFloat4 = br.ReadSingle();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54KineticSound : Dat54Sound
+ {
+ public MetaHash AudioHash { get; set; }
+ public float UnkFloat0 { get; set; } //Maybe kinetic force vector?
+ public float UnkFloat1 { get; set; }
+ public float UnkFloat2 { get; set; }
+
+ public Dat54KineticSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash };
+ UnkFloat0 = br.ReadSingle();
+ UnkFloat1 = br.ReadSingle();
+ UnkFloat2 = br.ReadSingle();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54SwitchSound : Dat54Sound
+ {
+ public MetaHash ParameterHash { get; set; } //0x0-0x4
+
+ public Dat54SwitchSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ ParameterHash = br.ReadUInt32();
+
+ ReadAudioTrackHashes(br);
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54VariableCurveSound : Dat54Sound
+ {
+ public MetaHash AudioHash { get; set; }
+ public MetaHash ParameterHash0 { get; set; } //0x4-0x8
+ public MetaHash ParameterHash1 { get; set; } //0x8-0xC
+ public MetaHash UnkCurvesHash { get; set; } //0xC-0x10
+
+ public Dat54VariableCurveSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash };
+ ParameterHash0 = br.ReadUInt32();
+ ParameterHash1 = br.ReadUInt32();
+ UnkCurvesHash = br.ReadUInt32();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54VariablePrintValueSound : Dat54Sound
+ {
+ public MetaHash ParameterHash { get; set; } //0x0-0x4
+ public string VariableString { get; set; }
+
+ public Dat54VariablePrintValueSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ ParameterHash = br.ReadUInt32();
+ VariableString = br.ReadString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54VariableBlockSound : Dat54Sound
+ {
+ public MetaHash AudioHash { get; set; }
+ public byte VariableCount { get; set; }
+ public Dat54VariableData[] Variables { get; set; }
+
+ public Dat54VariableBlockSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash };
+ VariableCount = br.ReadByte();
+ Variables = new Dat54VariableData[VariableCount];
+ for (int i = 0; i < VariableCount; i++)
+ {
+ Variables[i] = new Dat54VariableData(br);
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54VariableData
+ {
+ public MetaHash Name { get; set; }
+ public float Value { get; set; }
+ public float UnkFloat { get; set; }
+ public byte Flags { get; set; }
+
+ public Dat54VariableData(BinaryReader br)
+ {
+ Name = br.ReadUInt32();
+ Value = br.ReadSingle();
+ UnkFloat = br.ReadSingle();
+ Flags = br.ReadByte();
+ }
+
+ public override string ToString()
+ {
+ return Name + ": " + FloatUtil.ToString(Value) + ": " + FloatUtil.ToString(UnkFloat) + ": " + Flags.ToString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54IfSound : Dat54Sound
+ {
+ public MetaHash AudioHash1 { get; set; }
+ public MetaHash AudioHash2 { get; set; }
+ public MetaHash ParameterHash1 { get; set; }
+ public byte UnkByte { get; set; }
+ public float UnkFloat { get; set; }
+ public MetaHash ParameterHash2 { get; set; }
+
+ public Dat54IfSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash1 = br.ReadUInt32();
+ AudioHash2 = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash1, AudioHash2 };
+ ParameterHash1 = br.ReadUInt32();
+ UnkByte = br.ReadByte();
+ UnkFloat = br.ReadSingle();
+ ParameterHash2 = br.ReadUInt32();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54MathOperationSound : Dat54Sound
+ {
+ public MetaHash AudioHash { get; set; }
+ public byte UnkDataCount { get; set; }
+ public Dat54MathOperationSoundData[] UnkData { get; set; }
+
+ public Dat54MathOperationSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash };
+ UnkDataCount = br.ReadByte();
+ UnkData = new Dat54MathOperationSoundData[UnkDataCount];
+ for (int i = 0; i < UnkDataCount; i++)
+ {
+ UnkData[i] = new Dat54MathOperationSoundData(br);
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54MathOperationSoundData
+ {
+ public byte UnkByte { get; set; } //0x0-0x1
+ public int UnkInt0 { get; set; } //0x1-0x5
+ public int UnkInt1 { get; set; } //0x5-0x9
+ public int UnkInt2 { get; set; } //0x9-0xD
+ public int UnkInt3 { get; set; } //0xD-0x11
+ public int UnkInt4 { get; set; } //0x11-0x15
+ public MetaHash ParameterHash0 { get; set; } //0x15-0x19
+ public MetaHash ParameterHash1 { get; set; } //0x19-0x1D
+
+ public Dat54MathOperationSoundData(BinaryReader br)
+ {
+ UnkByte = br.ReadByte();
+ UnkInt0 = br.ReadInt32();
+ UnkInt1 = br.ReadInt32();
+ UnkInt2 = br.ReadInt32();
+ UnkInt3 = br.ReadInt32();
+ UnkInt4 = br.ReadInt32();
+ ParameterHash0 = br.ReadUInt32();
+ ParameterHash1 = br.ReadUInt32();
+ }
+
+ public override string ToString()
+ {
+ return ParameterHash0.ToString() + ", " + ParameterHash1.ToString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54ParameterTransformSound : Dat54Sound
+ {
+ public MetaHash AudioHash { get; set; }
+ public int ItemCount { get; set; }
+ public Dat54ParameterTransformSoundData[] Items { get; set; }
+
+ public Dat54ParameterTransformSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash };
+ ItemCount = br.ReadInt32(); //0x4-0x8
+ Items = new Dat54ParameterTransformSoundData[ItemCount];
+ for (int i = 0; i < ItemCount; i++)
+ {
+ Items[i] = new Dat54ParameterTransformSoundData(br);
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54ParameterTransformSoundData
+ {
+ public MetaHash ParameterHash { get; set; } //0x0-0x4
+ public float UnkFloat0 { get; set; } //0x4-0x8
+ public float UnkFloat1 { get; set; } //0x8-0xC
+ public int NestedDataCount { get; set; }
+ public Dat54ParameterTransformSoundData2[] NestedData { get; set; } //0x10..
+
+ public Dat54ParameterTransformSoundData(BinaryReader br)
+ {
+ ParameterHash = br.ReadUInt32();
+ UnkFloat0 = br.ReadSingle();
+ UnkFloat1 = br.ReadSingle();
+ NestedDataCount = br.ReadInt32();
+ NestedData = new Dat54ParameterTransformSoundData2[NestedDataCount];
+ for (int i = 0; i < NestedDataCount; i++)
+ {
+ NestedData[i] = new Dat54ParameterTransformSoundData2(br);
+ }
+ }
+
+ public override string ToString()
+ {
+ return ParameterHash.ToString() + ", " + NestedDataCount.ToString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54ParameterTransformSoundData2
+ {
+ public float UnkFloat0 { get; set; } //0x0-0x4
+ public int UnkInt { get; set; } //0x4
+ public MetaHash ParameterHash { get; set; } //0x8-0xC
+ public float UnkFloat1 { get; set; } //0xC
+ public float UnkFloat2 { get; set; } //0x10-0x14
+ public int NestedItemCount { get; set; }
+ public Vector2[] NestedItems { get; set; } //0x18-...
+
+ public Dat54ParameterTransformSoundData2(BinaryReader br)
+ {
+ UnkFloat0 = br.ReadSingle();
+ UnkInt = br.ReadInt32();
+ ParameterHash = br.ReadUInt32();
+ UnkFloat1 = br.ReadSingle();
+ UnkFloat2 = br.ReadSingle();
+ NestedItemCount = br.ReadInt32();
+ NestedItems = new Vector2[NestedItemCount];
+ for (int i = 0; i < NestedItemCount; i++)
+ {
+ NestedItems[i] = new Vector2(br.ReadSingle(), br.ReadSingle());
+ }
+ }
+
+ public override string ToString()
+ {
+ return ParameterHash.ToString() + ", " + NestedItemCount.ToString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54FluctuatorSound : Dat54Sound
+ {
+ public MetaHash AudioHash { get; set; }
+ public int ItemCount { get; set; }
+ public Dat54FluctuatorSoundData[] Items { get; set; }
+
+ public Dat54FluctuatorSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash };
+ ItemCount = br.ReadInt32(); //0x4-0x8
+ Items = new Dat54FluctuatorSoundData[ItemCount];
+ for (int i = 0; i < ItemCount; i++)
+ {
+ Items[i] = new Dat54FluctuatorSoundData(br);
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54FluctuatorSoundData
+ {
+ public byte UnkByte0 { get; set; } //0x0-0x1
+ public byte UnkByte1 { get; set; } //0x1-0x2
+ public MetaHash ParameterHash { get; set; } //0x2-0x6
+ public float UnkFloat00 { get; set; } //0x6-0xA
+ public float UnkFloat01 { get; set; } //0xA-0xE
+ public float UnkFloat02 { get; set; } //0xE-0x12
+ public float UnkFloat03 { get; set; } //0x12-0x16
+ public float UnkFloat04 { get; set; } //0x16-0x1A
+ public float UnkFloat05 { get; set; } //0x1A-0x1E
+ public float UnkFloat06 { get; set; } //0x1E-0x22
+ public float UnkFloat07 { get; set; } //0x22-0x26
+ public float UnkFloat08 { get; set; } //0x26-0x2A
+ public float UnkFloat09 { get; set; } //0x2A-0x2E
+ public float UnkFloat10 { get; set; } //0x2E-0x32
+
+ public Dat54FluctuatorSoundData(BinaryReader br)
+ {
+ UnkByte0 = br.ReadByte();
+ UnkByte1 = br.ReadByte();
+ ParameterHash = br.ReadUInt32();
+ UnkFloat00 = br.ReadSingle();
+ UnkFloat01 = br.ReadSingle();
+ UnkFloat02 = br.ReadSingle();
+ UnkFloat03 = br.ReadSingle();
+ UnkFloat04 = br.ReadSingle();
+ UnkFloat05 = br.ReadSingle();
+ UnkFloat06 = br.ReadSingle();
+ UnkFloat07 = br.ReadSingle();
+ UnkFloat08 = br.ReadSingle();
+ UnkFloat09 = br.ReadSingle();
+ UnkFloat10 = br.ReadSingle();
+ }
+
+ public override string ToString()
+ {
+ return ParameterHash.ToString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54AutomationSound : Dat54Sound
+ {
+ public MetaHash AudioHash0 { get; set; }
+ public float UnkFloat0 { get; set; } //0x4-0x8
+ public float UnkFloat1 { get; set; } //0x8-0xC
+ public MetaHash ParameterHash { get; set; } //0xC-0x10
+ public MetaHash AudioHash1 { get; set; }
+ public int WaveSlotId { get; set; } //0x14-0x18
+ public MetaHash UnkHash1 { get; set; } //0x18-0x1C
+ public int UnkDataCount { get; set; } // array data count 0x1C-0x20
+ public Dat54AutomationSoundData[] UnkData { get; set; } //0x20-
+
+ public Dat54AutomationSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ AudioHash0 = br.ReadUInt32();
+ UnkFloat0 = br.ReadSingle();
+ UnkFloat1 = br.ReadSingle();
+ ParameterHash = br.ReadUInt32();
+ AudioHash1 = br.ReadUInt32();
+ AudioTrackHashes = new[] { AudioHash0, AudioHash1 };
+ WaveSlotId = br.ReadInt32();
+ UnkHash1 = br.ReadUInt32();
+ UnkDataCount = br.ReadInt32();
+ UnkData = new Dat54AutomationSoundData[UnkDataCount];
+ for (int i = 0; i < UnkDataCount; i++)
+ {
+ UnkData[i] = new Dat54AutomationSoundData(br);
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54AutomationSoundData
+ {
+ public int UnkInt { get; set; } //0x0-0x1
+ public MetaHash UnkHash { get; set; } //0x2-0x6
+
+ public Dat54AutomationSoundData(BinaryReader br)
+ {
+ UnkInt = br.ReadInt32();
+ UnkHash = br.ReadUInt32();
+ }
+
+ public override string ToString()
+ {
+ return UnkInt.ToString() + ", " + UnkHash.ToString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54ExternalStreamSound : Dat54Sound
+ {
+ public Dat54ExternalStreamSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ ReadAudioTrackHashes(br);
+
+ //FlagsUint u1 = br.ReadUInt32();
+ //FlagsUint u2 = br.ReadUInt32();
+ //FlagsUint u3 = br.ReadUInt32();
+ //FlagsUint u4 = br.ReadUInt32();
+
+ //TODO: could be more to read!
+ if (br.BaseStream.Position != br.BaseStream.Length)
+ { } //hits here!
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54SoundSet : Dat54Sound
+ {
+ public int ItemCount { get; set; }
+ public Dat54SoundSetItem[] Items { get; set; }
+
+ public Dat54SoundSet(RelData d, BinaryReader br) : base(d, br)
+ {
+ ItemCount = br.ReadInt32();
+ Items = new Dat54SoundSetItem[ItemCount];
+ AudioTrackHashes = new MetaHash[ItemCount];
+ for (int i = 0; i < ItemCount; i++)
+ {
+ Items[i] = new Dat54SoundSetItem(br);
+ AudioTrackHashes[i] = Items[i].SoundName;
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54SoundSetItem
+ {
+ public MetaHash ScriptName { get; set; }
+ public MetaHash SoundName { get; set; }
+
+ public Dat54SoundSetItem(BinaryReader br)
+ {
+ ScriptName = br.ReadUInt32();
+ SoundName = br.ReadUInt32();
+ }
+
+ public override string ToString()
+ {
+ return ScriptName.ToString() + ": " + SoundName.ToString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54UnknownSound : Dat54Sound
+ {
+ public byte UnkDataCount { get; set; }
+ public Dat54UnknownSoundData[] UnkData { get; set; }
+
+ public Dat54UnknownSound(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkDataCount = br.ReadByte();
+ UnkData = new Dat54UnknownSoundData[UnkDataCount];
+ AudioTrackHashes = new MetaHash[UnkDataCount];
+ for (int i = 0; i < UnkDataCount; i++)
+ {
+ UnkData[i] = new Dat54UnknownSoundData(br);
+ AudioTrackHashes[i] = br.ReadUInt32();
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54UnknownSoundData
+ {
+ public byte UnkByte0 { get; set; }
+ public byte UnkByte1 { get; set; }
+ public byte UnkByte2 { get; set; }
+
+ public Dat54UnknownSoundData(BinaryReader br)
+ {
+ UnkByte0 = br.ReadByte();
+ UnkByte1 = br.ReadByte();
+ UnkByte2 = br.ReadByte();
+ }
+
+ public override string ToString()
+ {
+ return UnkByte0.ToString() + ": " + UnkByte1.ToString() + ": " + UnkByte2.ToString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54UnknownSound2 : Dat54Sound
+ {
+ public uint UnkCount { get; set; }
+ public MetaHash[] UnkItems { get; set; }
+
+ public Dat54UnknownSound2(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkCount = br.ReadUInt32();
+ UnkItems = new MetaHash[UnkCount];
+ for (int i = 0; i < UnkCount; i++)
+ {
+ UnkItems[i] = br.ReadUInt32();
+ }
+ }
+ }
+ [TC(typeof(EXP))] public class Dat54SoundList : Dat54Sound
+ {
+ public ushort UnkShort { get; set; }
+ public uint Count { get; set; }
+ public MetaHash[] Items { get; set; }
+
+ public Dat54SoundList(RelData d, BinaryReader br) : base(d, br)
+ {
+ UnkShort = br.ReadUInt16();
+ Count = br.ReadUInt32();
+ Items = new MetaHash[Count];
+ for (int i = 0; i < Count; i++)
+ {
+ Items[i] = br.ReadUInt32();
+ }
+ if (br.BaseStream.Position != br.BaseStream.Length)
+ { }
+ }
+ }
+
+
+
+
+
+ public enum Dat151RelType : byte //not sure how correct these are?
+ {
+ Collision = 1, //maybe for vehicle
+ Unk2 = 2,
+ Vehicle = 3,
+ VehicleEngine = 4,
+ Entity = 5, //not sure about this
+ Stream = 6,//possibly, generic audio stream
+ Unk7 = 7,
+ Helicopter = 8, //maybe
+ Unk9 = 9,
+ Unk11 = 11,
+ Unk12 = 12,
+ Unk13 = 13,
+ SpeechParams = 14,
+ Unk15 = 15,
+ Unk16 = 16,
+ Weapon = 17,
+ Unk18 = 18,
+ Unk22 = 22,
+ Unk23 = 23,
+ RadioStationsDLC = 24, //
+ RadioDLC = 25,
+ DLCMusic = 26,
+ Unk27 = 27,
+ Unk28 = 28,
+ Unk29 = 29,
+ PedPVG = 30, //maybe Ped Voice Group?
+ Unk31 = 31,
+ AmbientEmitterList = 32,
+ Unk33 = 33,
+ Unk35 = 35,
+ Unk36 = 36,
+ AmbientZone = 37,
+ AmbientEmitter = 38,
+ AmbientZoneList = 39,
+ Unk40 = 40,
+ Unk41 = 41,
+ Unk42 = 42,
+ Interior = 44,
+ Unk45 = 45,
+ InteriorRoom = 46,
+ Unk47 = 47,
+ Unk48 = 48,
+ Unk49 = 49,
+ WeaponAudioItem = 50,
+ Unk51 = 51,
+ Mod = 52, //what actually is a "mod" here? a change in some audio settings maybe?
+ Unk53 = 53,
+ Unk54 = 54,
+ Unk55 = 55,
+ Unk56 = 56,
+ Aeroplane = 57,
+ Unk59 = 59,
+ Mood = 62,
+ StartTrackAction = 63,
+ StopTrackAction = 64,
+ SetMoodAction = 65,
+ PlayerAction = 66,
+ StartOneShotAction = 67,
+ StopOneShotAction = 68,
+ Unk69 = 69,
+ Unk70 = 70,
+ Unk71 = 71,
+ Unk72 = 72,
+ AnimalParams = 73,
+ Unk74 = 74,
+ Unk75 = 75,
+ VehicleScannerParams = 76, //maybe not just vehicle
+ Unk77 = 77,
+ Unk78 = 78,
+ Unk79 = 79,
+ Unk80 = 80,
+ Unk81 = 81,
+ Unk82 = 82,
+ Unk83 = 83,
+ Unk84 = 84,
+ Unk85 = 85,
+ Unk86 = 86,
+ Explosion = 87,
+ VehicleEngineGranular = 88, //maybe not just vehicle
+ ShoreLinePool = 90,
+ ShoreLineLake = 91,
+ ShoreLineRiver = 92,
+ ShoreLineOcean = 93,
+ ShoreLineList = 94,
+ Unk95 = 95,
+ Unk96 = 96,
+ RadioDjSpeechAction = 98,
+ Unk99 = 99,
+ Unk100 = 100,
+ Unk101 = 101,
+ FadeOutRadioAction = 102,
+ FadeInRadioAction = 103,
+ ForceRadioTrackAction = 104,
+ Unk105 = 105,
+ Unk106 = 106,
+ Unk107 = 107,
+ Unk108 = 108,
+ Unk109 = 109,
+ Unk110 = 110,
+ Unk111 = 111,
+ Unk112 = 112,
+ Unk113 = 113,
+ Unk114 = 114,
+ Unk115 = 115,
+ Unk116 = 116,
+ Unk117 = 117,
+ Unk118 = 118,
+ Unk119 = 119,
+ Unk120 = 120,
+ Unk121 = 121,
+ }
+
+ [TC(typeof(EXP))] public class Dat151RelData : RelData
+ {
+ public Dat151RelType Type { get; set; }
+
+
+ public static int TotCount = 0; //###############DEBUGG
+ public static List FoundCoords = new List(); //###############DEBUGG
+ public void RecVec(Vector3 v)
+ {
+ float tol = 20.0f;
+ if ((Math.Abs(v.X)>tol) || (Math.Abs(v.Y)>tol) || (Math.Abs(v.Z)>tol))
+ {
+ FoundCoords.Add(FloatUtil.GetVector3String(v) + ", " + GetNameString());
+ }
+ }
+
+
+
+ public Dat151RelData() { }
+ public Dat151RelData(RelData d, BinaryReader br) : base(d)
+ {
+ Type = (Dat151RelType)TypeID;
+ }
+
+ public override string ToString()
+ {
+ return GetBaseString() + ": " + Type.ToString();
+ }
+ }
+ [TC(typeof(EXP))] public class Dat151Sound : RelSound
+ {
+ public Dat151RelType Type { get; set; }
+
+ public Dat151Sound(RelData d, BinaryReader br) : base(d, br)
+ {
+ Type = (Dat151RelType)TypeID;
+ }
+
+ public override string ToString()
+ {
+ return GetBaseString() + ": " + Type.ToString();
+ }
+ }
+
+ public enum Dat151ZoneShape : uint
+ {
+ Box = 0,
+ Sphere = 1,
+ Line = 2,
+ }
+
+ [TC(typeof(EXP))] public class Dat151AmbientEmitterList : Dat151RelData
+ {
+ public uint UnkOffset0 { get; set; }
+ public uint EmitterCount { get; set; }
+ public MetaHash[] EmitterHashes { get; set; }
+
+ public Dat151AmbientEmitterList(RelData d, BinaryReader br) : base(d, br)
+ {
+ br.BaseStream.Position = 0; //1 byte was read already (TypeID)
+
+ UnkOffset0 = ((br.ReadUInt32() >> 8) & 0xFFFFFF);
+ EmitterCount = br.ReadUInt32();
+ EmitterHashes = new MetaHash[EmitterCount];
+ for (int i = 0; i < EmitterCount; i++)
+ {
+ EmitterHashes[i] = br.ReadUInt32();
+ }
+
+ long bytesleft = br.BaseStream.Length - br.BaseStream.Position;
+ if (bytesleft != 0)
+ { } //no hits here
+
+ }
+ }
+ [TC(typeof(EXP))] public class Dat151AmbientZone : Dat151RelData
+ {
+ public uint UnkOffset0 { get; set; }
+ public FlagsUint Flags00 { get; set; }
+ public Dat151ZoneShape Shape { get; set; }
+ public FlagsUint Flags02 { get; set; }
+ public Vector3 OuterPos { get; set; }
+ public float Unused01 { get; set; }
+ public Vector3 OuterSize { get; set; }
+ public float Unused02 { get; set; }
+ public Vector4 OuterVec1 { get; set; }
+ public Vector4 OuterVec2 { get; set; }
+ public uint OuterAngle { get; set; }
+ public Vector3 OuterVec3 { get; set; }
+ public Vector3 InnerPos { get; set; }
+ public float Unused06 { get; set; }
+ public Vector3 InnerSize { get; set; }
+ public float Unused07 { get; set; }
+ public Vector4 InnerVec1 { get; set; }
+ public Vector4 InnerVec2 { get; set; }
+ public uint InnerAngle { get; set; }
+ public Vector3 InnerVec3 { get; set; }
+ public Vector4 Vec11 { get; set; }
+ public Vector4 Vec12 { get; set; }
+ public Vector4 Vec13 { get; set; }
+
+ public FlagsUint Flags05 { get; set; }
+ public byte Unk14 { get; set; }
+ public byte Unk15 { get; set; }
+ public ushort HashesCount { get; set; }
+ public byte Unk16 { get; set; }
+ public MetaHash[] Hashes { get; set; }
+
+ public uint ExtParamsCount { get; set; }
+ public ExtParam[] ExtParams { get; set; }
+ public struct ExtParam
+ {
+ public MetaHash Hash { get; set; }
+ public float Value { get; set; }
+ public ExtParam(BinaryReader br)
+ {
+ Hash = br.ReadUInt32();
+ Value = br.ReadSingle();
+ }
+ public override string ToString()
+ {
+ return Hash.ToString() + ": " + FloatUtil.ToString(Value);
+ }
+ }
+
+
+
+ public Dat151AmbientZone(RelData d, BinaryReader br) : base(d, br)
+ {
+ br.BaseStream.Position = 0; //1 byte was read already (TypeID)
+
+ UnkOffset0 = ((br.ReadUInt32() >> 8) & 0xFFFFFF);
+ Flags00 = br.ReadUInt32();
+ Shape = (Dat151ZoneShape)br.ReadUInt32();
+ Flags02 = br.ReadUInt32();
+ OuterPos = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Unused01 = br.ReadSingle();
+ OuterSize = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Unused02 = br.ReadSingle();
+ OuterVec1 = new Vector4(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ OuterVec2 = new Vector4(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ OuterAngle = br.ReadUInt32();//###
+ OuterVec3 = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ InnerPos = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Unused06 = br.ReadSingle();
+ InnerSize = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Unused07 = br.ReadSingle();
+ InnerVec1 = new Vector4(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ InnerVec2 = new Vector4(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ InnerAngle = br.ReadUInt32();//###
+ InnerVec3 = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Vec11 = new Vector4(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Vec12 = new Vector4(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Vec13 = new Vector4(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+
+ Flags05 = br.ReadUInt32();
+ Unk14 = br.ReadByte();
+ Unk15 = br.ReadByte();
+ HashesCount = br.ReadByte();
+ Unk16 = br.ReadByte();
+ Hashes = new MetaHash[HashesCount];
+ for (int i = 0; i < HashesCount; i++)
+ {
+ Hashes[i] = br.ReadUInt32();
+ }
+
+ ExtParamsCount = br.ReadUInt32();
+ ExtParams = new ExtParam[ExtParamsCount];
+ for (int i = 0; i < ExtParamsCount; i++)
+ {
+ ExtParams[i] = new ExtParam(br);
+ }
+ if (ExtParamsCount != 0)
+ { }
+
+ var data = this.Data;
+
+ long bytesleft = br.BaseStream.Length - br.BaseStream.Position;
+ if (bytesleft != 0)
+ {
+ //byte[] remainder = br.ReadBytes((int)bytesleft);
+ //for (int i = 0; i < remainder.Length; i++)
+ //{
+ // if (remainder[i] != 0)
+ // { } //no hits here! probably got everything, i'm assuming the block is padded to 0x10 or something.
+ //}
+ }
+
+
+ //RecVec(Pos01);//debug coords output
+ //RecVec(Pos06);
+
+
+ if (Unused01 != 0)
+ { }//no hit
+ if (Unused02 != 0)
+ { }//no hit
+ if (Unused06 != 0)
+ { }//no hit
+ if (Unused07 != 0)
+ { }//no hit
+ if (Shape != 0)
+ { }//eg 1, 2
+ if (Flags02.Value != 0)
+ { }//no hit
+ if (OuterAngle > 360)
+ { }//no hit
+ if (InnerAngle > 360)
+ { }//no hit
+ if (Flags05.Value != 0)
+ { }//eg 0xAE64583B, 0x61083310, 0xCAE96294, 0x1C376176
+ }
+
+ }
+ [TC(typeof(EXP))] public class Dat151AmbientEmitter : Dat151RelData
+ {
+ public uint UnkOffset0 { get; set; }
+ public FlagsUint Unk00 { get; set; }
+ public FlagsUint Unk01 { get; set; }
+ public FlagsUint Unk02 { get; set; }
+ public Vector3 Position { get; set; }
+ public FlagsUint Unk03 { get; set; } //0
+ public MetaHash Unk04 { get; set; }
+ public MetaHash Unk05 { get; set; }
+ public FlagsUint Unk06 { get; set; } //0
+ public FlagsUint Unk07 { get; set; } //0xFFFFFFFF
+ public FlagsUint Unk08 { get; set; } //0
+ public float Unk09 { get; set; } //1, 5, 100, ...
+ public float InnerRad { get; set; } //0, 4, ... 100 ... min value?
+ public float OuterRad { get; set; } //15, 16, 12, 10, 20, 300 ... max value?
+ public FlagsByte Unk12 { get; set; }
+ public FlagsByte Unk13 { get; set; } //0,1,2,3,4,5
+ public FlagsByte Unk14 { get; set; }
+ public FlagsByte Unk15 { get; set; } //0,1,2,3,4,5
+ public FlagsUshort Unk16 { get; set; } //0..600
+ public FlagsUshort Unk17 { get; set; } //0..150
+ public FlagsByte Unk18 { get; set; } //0,1,2
+ public FlagsByte Unk19 { get; set; } //0,1,2
+ public FlagsByte Unk20 { get; set; } //1,2,3,4,8,255
+ public FlagsByte Unk21 { get; set; } //1,2,3,4,5,6,8,10,255
+ public FlagsByte Unk22 { get; set; } //0, 50, 80, 100
+ public FlagsByte Unk23 { get; set; } //1,2,3,5
+ public ushort ExtParamCount { get; set; } //0,1,2,4
+ public ExtParam[] ExtParams { get; set; }
+
+ public struct ExtParam
+ {
+ public MetaHash Hash;
+ public float Value;
+ public uint Flags;
+ public ExtParam(BinaryReader br)
+ {
+ Hash = br.ReadUInt32();
+ Value = br.ReadSingle();
+ Flags = br.ReadUInt32();
+ }
+ public override string ToString()
+ {
+ return Hash.ToString() + ": " + FloatUtil.ToString(Value) + ": " + Flags.ToString();
+ }
+ }
+
+
+ public Dat151AmbientEmitter(RelData d, BinaryReader br) : base(d, br)
+ {
+ br.BaseStream.Position = 0; //1 byte was read already (TypeID)
+
+ UnkOffset0 = ((br.ReadUInt32() >> 8) & 0xFFFFFF);
+ Unk00 = br.ReadUInt32();
+ Unk01 = br.ReadUInt32();
+ Unk02 = br.ReadUInt32();
+ Position = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
+ Unk03 = br.ReadUInt32(); //0
+ Unk04 = br.ReadUInt32();
+ Unk05 = br.ReadUInt32();
+ Unk06 = br.ReadUInt32(); //0
+ Unk07 = br.ReadUInt32(); //0xFFFFFFFF
+ Unk08 = br.ReadUInt32(); //0
+ Unk09 = br.ReadSingle(); //1, 5, 100, ...
+ InnerRad = br.ReadSingle(); //0, 4, ... 100 ... min value?
+ OuterRad = br.ReadSingle(); //15, 16, 12, 10, 20, 300 ... max value?
+ Unk12 = br.ReadByte();
+ Unk13 = br.ReadByte(); //0,1,2,3,4,5
+ Unk14 = br.ReadByte();
+ Unk15 = br.ReadByte(); //0,1,2,3,4,5
+ Unk16 = br.ReadUInt16(); //0..600
+ Unk17 = br.ReadUInt16(); //0..150
+ Unk18 = br.ReadByte(); //0,1,2
+ Unk19 = br.ReadByte(); //0,1,2
+ Unk20 = br.ReadByte(); //1,2,3,4,8,255
+ Unk21 = br.ReadByte(); //1,2,3,4,5,6,8,10,255
+ Unk22 = br.ReadByte(); //0, 50, 80, 100
+ Unk23 = br.ReadByte(); //1,2,3,5
+ ExtParamCount = br.ReadUInt16(); //0,1,2,4
+
+ if (ExtParamCount > 0)
+ {
+ ExtParams = new ExtParam[ExtParamCount];
+ for (int i = 0; i < ExtParamCount; i++)
+ {
+ ExtParams[i] = new ExtParam(br);
+ }
+ //array seems to be padded to multiples of 16 bytes. (read the rest here)
+ int brem = (16 - ((ExtParamCount * 12) % 16)) % 16;
+ if (brem > 0)
+ {
+ byte[] brema = br.ReadBytes(brem);
+ //for (int i = 0; i < brem; i++)
+ //{
+ // if (brema[i] != 0)
+ // { } //check all remaining bytes are 0 - never hit here
+ //}
+ }
+ }
+
+
+ switch (Unk12.Value)//no pattern?
+ {
+ default:
+ break;
+ }
+ switch (Unk13.Value)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ break;
+ default:
+ break;
+ }
+ switch (Unk14.Value)//no pattern?
+ {
+ default:
+ break;
+ }
+ switch (Unk15.Value)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ break;
+ default:
+ break;
+ }
+ switch (Unk16.Value)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 18:
+ case 20:
+ case 22:
+ case 24:
+ case 25:
+ case 26:
+ case 30:
+ case 32:
+ case 35:
+ case 40:
+ case 45:
+ case 48:
+ case 50:
+ case 51:
+ case 54:
+ case 55:
+ case 57:
+ case 60:
+ case 64:
+ case 65:
+ case 70:
+ case 75:
+ case 80:
+ case 90:
+ case 95:
+ case 97:
+ case 100:
+ case 120:
+ case 125:
+ case 130:
+ case 135:
+ case 140:
+ case 145:
+ case 150:
+ case 160:
+ case 170:
+ case 178:
+ case 180:
+ case 190:
+ case 200:
+ case 220:
+ case 225:
+ case 240:
+ case 245:
+ case 250:
+ case 300:
+ case 350:
+ case 500:
+ case 600:
+ break;
+ default:
+ break;
+ }
+ switch (Unk17.Value)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 12:
+ case 15:
+ case 17:
+ case 20:
+ case 21:
+ case 22:
+ case 25:
+ case 27:
+ case 30:
+ case 32:
+ case 35:
+ case 40:
+ case 50:
+ case 60:
+ case 100:
+ case 150:
+ break;
+ default:
+ break;
+ }
+ switch (Unk18.Value)
+ {
+ case 0:
+ case 1:
+ case 2:
+ break;
+ default:
+ break;
+ }
+ switch (Unk19.Value)
+ {
+ case 0:
+ case 1:
+ case 2:
+ break;
+ default:
+ break;
+ }
+ switch (Unk20.Value)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 8:
+ case 255:
+ break;
+ default:
+ break;
+ }
+ switch (Unk21.Value)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ case 10:
+ case 255:
+ break;
+ default:
+ break;
+ }
+ switch (Unk22.Value)
+ {
+ case 0:
+ case 50:
+ case 80:
+ case 100:
+ break;
+ default:
+ break;
+ }
+ switch (Unk23.Value)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 5:
+ break;
+ default:
+ break;
+ }
+ switch (ExtParamCount)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 4:
+ break;
+ default:
+ break;
+ }
+
+
+
+ //if ((Position.X != 0) || (Position.Y != 0) || (Position.Z != 0))
+ //{
+ // FoundCoords.Add(FloatUtil.GetVector3String(Position) + ", " + GetNameString());
+ //}
+
+ long bytesleft = br.BaseStream.Length - br.BaseStream.Position;
+ if (bytesleft != 0)
+ { }
+ }
+
+ }
+ [TC(typeof(EXP))] public class Dat151AmbientZoneList : Dat151RelData
+ {
+ public uint UnkOffset0 { get; set; }
+ public uint ZoneCount { get; set; }
+ public MetaHash[] ZoneHashes { get; set; }
+
+ public Dat151AmbientZoneList(RelData d, BinaryReader br) : base(d, br)
+ {
+ br.BaseStream.Position = 0; //1 byte was read already (TypeID)
+
+ UnkOffset0 = ((br.ReadUInt32() >> 8) & 0xFFFFFF);
+ ZoneCount = br.ReadUInt32();
+ ZoneHashes = new MetaHash[ZoneCount];
+ for (int i = 0; i < ZoneCount; i++)
+ {
+ ZoneHashes[i] = br.ReadUInt32();
+ }
+
+ long bytesleft = br.BaseStream.Length - br.BaseStream.Position;
+ if (bytesleft != 0)
+ { } //no hits here
+
+ }
+
+ }
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/Stats.cs b/CodeWalker.Core/GameFiles/FileTypes/Stats.cs
new file mode 100644
index 0000000..09bacf7
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/Stats.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+
+
+ public static class StatsNames
+ {
+ public static Dictionary Index = new Dictionary();
+ private static object syncRoot = new object();
+
+ public static volatile bool FullIndexBuilt = false;
+
+ public static void Clear()
+ {
+ lock (syncRoot)
+ {
+ Index.Clear();
+ }
+ }
+
+ public static bool Ensure(string str)
+ {
+ uint hash = JenkHash.GenHash(str);
+ if (hash == 0) return true;
+ lock (syncRoot)
+ {
+ if (!Index.ContainsKey(hash))
+ {
+ Index.Add(hash, str);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static bool Ensure(string str, uint hash)
+ {
+ if (hash == 0) return true;
+ lock (syncRoot)
+ {
+ if (!Index.ContainsKey(hash))
+ {
+ Index.Add(hash, str);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static string GetString(uint hash)
+ {
+ string res;
+ lock (syncRoot)
+ {
+ if (!Index.TryGetValue(hash, out res))
+ {
+ res = hash.ToString();
+ }
+ }
+ return res;
+ }
+ public static string TryGetString(uint hash)
+ {
+ string res;
+ lock (syncRoot)
+ {
+ if (!Index.TryGetValue(hash, out res))
+ {
+ res = string.Empty;
+ }
+ }
+ return res;
+ }
+
+ }
+
+
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YbnFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YbnFile.cs
new file mode 100644
index 0000000..3e86c68
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YbnFile.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ public class YbnFile : GameFile, PackedFile
+ {
+ public Bounds Bounds { get; set; }
+
+ public YbnFile() : base(null, GameFileType.Ybn)
+ {
+ }
+ public YbnFile(RpfFileEntry entry) : base(entry, GameFileType.Ybn)
+ {
+ }
+
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+ RpfFileEntry = entry;
+
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ throw new Exception("File entry wasn't a resource! (is it binary data?)");
+ }
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+
+ Bounds = rd.ReadBlock();
+
+ Bounds.OwnerName = entry.Name;
+
+ Loaded = true;
+ }
+ }
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YcdFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YcdFile.cs
new file mode 100644
index 0000000..a2a6f04
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YcdFile.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class YcdFile : GameFile, PackedFile
+ {
+ public ClipDictionary ClipDictionary { get; set; }
+
+ public Dictionary ClipMap { get; set; }
+ public Dictionary AnimMap { get; set; }
+
+ public ClipMapEntry[] ClipMapEntries { get; set; }
+ public AnimationMapEntry[] AnimMapEntries { get; set; }
+
+
+ public YcdFile() : base(null, GameFileType.Ycd)
+ {
+ }
+ public YcdFile(RpfFileEntry entry) : base(entry, GameFileType.Ycd)
+ {
+ }
+
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+ RpfFileEntry = entry;
+ //Hash = entry.ShortNameHash;
+
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ throw new Exception("File entry wasn't a resource! (is it binary data?)");
+ }
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+
+ ClipDictionary = rd.ReadBlock();
+
+ ClipMap = new Dictionary();
+ AnimMap = new Dictionary();
+ if (ClipDictionary != null)
+ {
+ if ((ClipDictionary.Clips != null) && (ClipDictionary.Clips.data_items != null))
+ {
+ foreach (var cme in ClipDictionary.Clips.data_items)
+ {
+ if (cme != null)
+ {
+ ClipMap[cme.Hash] = cme;
+ var nxt = cme.Next;
+ while (nxt != null)
+ {
+ ClipMap[nxt.Hash] = nxt;
+ nxt = nxt.Next;
+ }
+ }
+ }
+ }
+ if ((ClipDictionary.Animations != null) && (ClipDictionary.Animations.Animations != null) && (ClipDictionary.Animations.Animations.data_items != null))
+ {
+ foreach (var ame in ClipDictionary.Animations.Animations.data_items)
+ {
+ if (ame != null)
+ {
+ AnimMap[ame.Hash] = ame;
+ var nxt = ame.NextEntry;
+ while (nxt != null)
+ {
+ AnimMap[nxt.Hash] = nxt;
+ nxt = nxt.NextEntry;
+ }
+ }
+ }
+ }
+ }
+
+ foreach (var cme in ClipMap.Values)
+ {
+ var clip = cme.Clip;
+ if (clip == null) continue;
+ clip.Ycd = this;
+ if (string.IsNullOrEmpty(clip.Name)) continue;
+ string name = clip.Name.Replace('\\', '/');
+ var slidx = name.LastIndexOf('/');
+ if ((slidx >= 0) && (slidx < name.Length - 1))
+ {
+ name = name.Substring(slidx + 1);
+ }
+ var didx = name.LastIndexOf('.');
+ if ((didx > 0) && (didx < name.Length))
+ {
+ name = name.Substring(0, didx);
+ }
+ clip.ShortName = name;
+ name = name.ToLowerInvariant();
+ JenkIndex.Ensure(name);
+
+
+ //if (name.EndsWith("_uv_0")) //hash for these entries match string with this removed, +1
+ //{
+ //}
+ //if (name.EndsWith("_uv_1")) //same as above, but +2
+ //{
+ //}
+
+ }
+ foreach (var ame in AnimMap.Values)
+ {
+ var anim = ame.Animation;
+ if (anim == null) continue;
+ anim.Ycd = this;
+ }
+
+
+ ClipMapEntries = ClipMap.Values.ToArray();
+ AnimMapEntries = AnimMap.Values.ToArray();
+
+
+ }
+ }
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YddFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YddFile.cs
new file mode 100644
index 0000000..6987892
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YddFile.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YddFile : GameFile, PackedFile
+ {
+ public DrawableDictionary DrawableDict { get; set; }
+
+ public Dictionary Dict { get; set; }
+ public Drawable[] Drawables { get; set; }
+
+ public YddFile() : base(null, GameFileType.Ydd)
+ {
+ }
+ public YddFile(RpfFileEntry entry) : base(entry, GameFileType.Ydd)
+ {
+ }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+ RpfFileEntry = entry;
+
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ throw new Exception("File entry wasn't a resource! (is it binary data?)");
+ }
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+ DrawableDict = rd.ReadBlock();
+
+ //MemoryUsage = 0; //uses decompressed filesize now...
+ //if (DrawableDict != null)
+ //{
+ // MemoryUsage += DrawableDict.MemoryUsage;
+ //}
+
+ if ((DrawableDict != null) &&
+ (DrawableDict.Drawables != null) &&
+ (DrawableDict.Drawables.data_items != null) &&
+ (DrawableDict.Hashes != null))
+ {
+ Dict = new Dictionary();
+ var drawables = DrawableDict.Drawables.data_items;
+ var hashes = DrawableDict.Hashes;
+ for (int i = 0; (i < drawables.Length) && (i < hashes.Length); i++)
+ {
+ var drawable = drawables[i];
+ var hash = hashes[i];
+ Dict[hash] = drawable;
+ drawable.Owner = this;
+ }
+
+
+ for (int i = 0; (i < drawables.Length) && (i < hashes.Length); i++)
+ {
+ var drawable = drawables[i];
+ var hash = hashes[i];
+ if ((drawable.Name == null) || (drawable.Name.EndsWith("#dd")))
+ {
+ string hstr = JenkIndex.TryGetString(hash);
+ if (!string.IsNullOrEmpty(hstr))
+ {
+ drawable.Name = hstr;
+ }
+ else
+ { }
+ }
+ }
+
+ Drawables = Dict.Values.ToArray();
+
+ }
+
+ Loaded = true;
+
+ }
+
+ }
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YdrFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YdrFile.cs
new file mode 100644
index 0000000..900646d
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YdrFile.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ public class YdrFile : GameFile, PackedFile
+ {
+ public Drawable Drawable { get; set; }
+
+ public YdrFile() : base(null, GameFileType.Ydr)
+ {
+ }
+ public YdrFile(RpfFileEntry entry) : base(entry, GameFileType.Ydr)
+ {
+ }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+ RpfFileEntry = entry;
+
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ throw new Exception("File entry wasn't a resource! (is it binary data?)");
+ }
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+ //MemoryUsage = 0;
+
+ try
+ {
+ Drawable = rd.ReadBlock();
+ Drawable.Owner = this;
+ //MemoryUsage += Drawable.MemoryUsage; //uses decompressed filesize now...
+ }
+ catch (Exception ex)
+ {
+ string err = ex.ToString();
+ }
+
+ Loaded = true;
+
+ }
+
+
+ }
+
+
+
+
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YftFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YftFile.cs
new file mode 100644
index 0000000..5c2d01b
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YftFile.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ public class YftFile : GameFile, PackedFile
+ {
+ public FragType Fragment { get; set; }
+
+ public YftFile() : base(null, GameFileType.Yft)
+ {
+ }
+ public YftFile(RpfFileEntry entry) : base(entry, GameFileType.Yft)
+ {
+ }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+ RpfFileEntry = entry;
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ throw new Exception("File entry wasn't a resource! (is it binary data?)");
+ }
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+ Fragment = rd.ReadBlock();
+
+ if (Fragment.Drawable != null)
+ {
+ Fragment.Drawable.Owner = this;
+ }
+ if (Fragment.Unknown_F8h_Data != null)
+ {
+ Fragment.Unknown_F8h_Data.Owner = this;
+ }
+
+ Loaded = true;
+ }
+
+
+ }
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YmapFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YmapFile.cs
new file mode 100644
index 0000000..7e61049
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YmapFile.cs
@@ -0,0 +1,1681 @@
+using SharpDX;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class YmapFile : GameFile, PackedFile
+ {
+
+ public Meta Meta { get; set; }
+ public PsoFile Pso { get; set; }
+ public RbfFile Rbf { get; set; }
+
+ public CMapData _CMapData;
+
+ public CMapData CMapData { get { return _CMapData; } set { _CMapData = value; } }
+ public CEntityDef[] CEntityDefs { get; set; }
+ public CMloInstanceDef[] CMloInstanceDefs { get; set; }
+ public CCarGen[] CCarGens { get; set; }
+ public CTimeCycleModifier[] CTimeCycleModifiers { get; set; }
+ public MetaHash[] physicsDictionaries { get; set; }
+
+ public Unk_975711773[] CBoxOccluders { get; set; }
+ public Unk_2741784237[] COccludeModels { get; set; }
+
+
+ public string[] Strings { get; set; }
+ public YmapEntityDef[] AllEntities;
+ public YmapEntityDef[] RootEntities;
+ public YmapEntityDef[] MloEntities;
+
+ public YmapFile[] ChildYmaps = null;
+ public bool MergedWithParent = false;
+
+ public YmapGrassInstanceBatch[] GrassInstanceBatches { get; set; }
+ public YmapPropInstanceBatch[] PropInstanceBatches { get; set; }
+
+ public YmapDistantLODLights DistantLODLights { get; set; }
+
+ public YmapTimeCycleModifier[] TimeCycleModifiers { get; set; }
+
+ public YmapCarGen[] CarGenerators { get; set; }
+
+ public YmapBoxOccluder[] BoxOccluders { get; set; }
+ public YmapOccludeModel[] OccludeModels { get; set; }
+
+
+ //fields used by the editor:
+ public bool HasChanged { get; set; } = false;
+ public List SaveWarnings = null;
+
+
+
+
+ public YmapFile() : base(null, GameFileType.Ymap)
+ {
+ }
+ public YmapFile(RpfFileEntry entry) : base(entry, GameFileType.Ymap)
+ {
+ RpfFileEntry = entry;
+ }
+
+ public void Load(byte[] data)
+ {
+ //direct load from a raw, compressed ymap file (openIV-compatible format)
+
+ RpfFile.LoadResourceFile(this, data, 2);
+
+ Loaded = true;
+ }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+ RpfFileEntry = entry;
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ NonMetaLoad(data);
+ return;
+ }
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+ Meta = rd.ReadBlock();
+
+
+
+ CMapData = MetaTypes.GetTypedData(Meta, MetaName.CMapData);
+
+
+
+ Strings = MetaTypes.GetStrings(Meta);
+ if (Strings != null)
+ {
+ foreach (string str in Strings)
+ {
+ JenkIndex.Ensure(str); //just shove them in there
+ }
+ }
+
+ physicsDictionaries = MetaTypes.GetHashArray(Meta, CMapData.physicsDictionaries);
+
+
+ EnsureEntities(Meta); //load all the entity data and create the YmapEntityDefs
+
+ EnsureInstances(Meta);
+
+ EnsureLodLights(Meta);
+
+ EnsureDistantLODLights(Meta);
+
+ EnsureTimeCycleModifiers(Meta);
+
+ EnsureCarGens(Meta);
+
+ EnsureBoxOccluders(Meta);
+
+ EnsureOccludeModels(Meta);
+
+ EnsureContainerLods(Meta);
+
+
+ #region data block test and old code
+
+ //foreach (var block in Meta.DataBlocks)
+ //{
+ // switch (block.StructureNameHash)
+ // {
+ // case MetaName.STRING:
+ // case MetaName.POINTER:
+ // case MetaName.HASH:
+ // case MetaName.UINT:
+ // case MetaName.VECTOR3: //distant lod lights uses this
+ // case MetaName.CMapData:
+ // case MetaName.CEntityDef:
+ // case MetaName.CTimeCycleModifier: //these sections are handled already
+ // case MetaName.CCarGen:
+ // case MetaName.CLightAttrDef:
+ // case MetaName.CMloInstanceDef:
+ // case MetaName.CExtensionDefDoor:
+ // case MetaName.CExtensionDefLightEffect:
+ // case MetaName.CExtensionDefSpawnPointOverride:
+ // case MetaName.rage__fwGrassInstanceListDef: //grass instance buffer
+ // case MetaName.rage__fwGrassInstanceListDef__InstanceData: //grass instance buffer data
+ // break;
+ // case MetaName.PhVerletClothCustomBounds: //these sections still todo..
+ // case MetaName.SectionUNKNOWN1:
+ // case MetaName.SectionUNKNOWN5://occlusion vertex data container
+ // case MetaName.SectionUNKNOWN7://occlusion related?
+ // break;
+ // case (MetaName)17: //vertex data - occlusion related - SectionUNKNOWN5
+ // break;
+ // case (MetaName)33: //what is this? maybe lodlights related
+ // break;
+ // default:
+ // break;
+ // }
+ //}
+
+
+
+ //MetaTypes.ParseMetaData(Meta);
+
+ //string shortname = resentry.Name.Substring(0, resentry.Name.LastIndexOf('.'));
+ //uint namehash = JenkHash.GenHash(shortname);
+
+
+
+
+
+ //CLightAttrDefs = MetaTypes.GetTypedDataArray(Meta, MetaName.CLightAttrDef);
+ //if (CLightAttrDefs != null)
+ //{ }
+
+
+ //var unk5s = MetaTypes.GetTypedDataArray(Meta, MetaName.SectionUNKNOWN5);
+ //if (unk5s != null) //used in occlusion ymaps
+ //{
+ // foreach (var unk5 in unk5s)
+ // {
+ // if ((unk5.verts.Ptr > 0) && (unk5.verts.Ptr <= (ulong)Meta.DataBlocks.Length))
+ // {
+ // var indicesoffset = unk5.Unk_853977995;
+ // var datablock = Meta.DataBlocks[((int)unk5.verts.Ptr) - 1];
+ // if (datablock != null)
+ // { }//vertex data... occlusion mesh?
+ // }
+ // }
+ //}
+
+ //var unk7s = MetaTypes.GetTypedDataArray(Meta, MetaName.SectionUNKNOWN7);
+ //if (unk7s != null)
+ //{ } //used in occlusion ymaps
+
+ //var unk10s = MetaTypes.GetTypedDataArray(Meta, MetaName.SectionUNKNOWN10);
+ //if (unk10s != null)
+ //{ } //entity pointer array..
+
+ //CDoors = MetaTypes.GetTypedDataArray(Meta, MetaName.CExtensionDefDoor);
+ //if (CDoors != null)
+ //{ } //needs work - doors can be different types? not enough bytes for one
+
+ //CExtLightEffects = MetaTypes.GetTypedDataArray(Meta, MetaName.CExtensionDefLightEffect);
+ //if (CExtLightEffects != null)
+ //{ }
+
+ //CSpawnOverrides = MetaTypes.GetTypedDataArray(Meta, MetaName.CExtensionDefSpawnPointOverride);
+ //if (CSpawnOverrides != null)
+ //{ }
+
+ #endregion
+ }
+
+
+
+ private void NonMetaLoad(byte[] data)
+ {
+ //non meta not supported yet! but see what's in there...
+ MemoryStream ms = new MemoryStream(data);
+ if (RbfFile.IsRBF(ms))
+ {
+ var Rbf = new RbfFile();
+ Rbf.Load(ms);
+ }
+ else if (PsoFile.IsPSO(ms))
+ {
+ var Pso = new PsoFile();
+ Pso.Load(ms);
+ //PsoTypes.EnsurePsoTypes(Pso);
+ }
+ else
+ {
+ }
+
+ }
+
+
+
+ private void EnsureEntities(Meta Meta)
+ {
+ //CMloInstanceDefs = MetaTypes.ConvertDataArray(Meta, MetaName.CMloInstanceDef, CMapData.entities);
+ CMloInstanceDefs = MetaTypes.GetTypedDataArray(Meta, MetaName.CMloInstanceDef);
+ if (CMloInstanceDefs != null)
+ { }
+
+ var eptrs = MetaTypes.GetPointerArray(Meta, CMapData.entities);
+ //CEntityDefs = MetaTypes.ConvertDataArray(Meta, MetaName.CEntityDef, CMapData.entities);
+ CEntityDefs = MetaTypes.GetTypedDataArray(Meta, MetaName.CEntityDef);
+ if (CEntityDefs != null)
+ { }
+
+
+
+
+ int instcount = 0;
+ if (CEntityDefs != null) instcount += CEntityDefs.Length;
+ if (CMloInstanceDefs != null) instcount += CMloInstanceDefs.Length;
+
+ if (instcount > 0)
+ {
+
+ //build the entity hierarchy.
+ List roots = new List(instcount);
+ List alldefs = new List(instcount);
+ List mlodefs = null;
+
+ if (CEntityDefs != null)
+ {
+ for (int i = 0; i < CEntityDefs.Length; i++)
+ {
+ YmapEntityDef d = new YmapEntityDef(this, i, ref CEntityDefs[i]);
+ alldefs.Add(d);
+ }
+ }
+ if (CMloInstanceDefs != null)
+ {
+ mlodefs = new List();
+ for (int i = 0; i < CMloInstanceDefs.Length; i++)
+ {
+ YmapEntityDef d = new YmapEntityDef(this, i, ref CMloInstanceDefs[i]);
+ uint[] defentsets = MetaTypes.GetUintArray(Meta, CMloInstanceDefs[i].defaultEntitySets);
+ if (d.MloInstance != null)
+ {
+ d.MloInstance.defaultEntitySets = defentsets;
+ }
+ alldefs.Add(d);
+ mlodefs.Add(d);
+ }
+ }
+
+
+ for (int i = 0; i < alldefs.Count; i++)
+ {
+ YmapEntityDef d = alldefs[i];
+ int pind = d.CEntityDef.parentIndex;
+ bool isroot = false;
+ if ((pind < 0) || (pind >= alldefs.Count) || (pind >= i)) //index check? might be a problem
+ {
+ isroot = true;
+ }
+ else
+ {
+ YmapEntityDef p = alldefs[pind];
+ if ((p.CEntityDef.lodLevel <= d.CEntityDef.lodLevel) ||
+ ((p.CEntityDef.lodLevel == Unk_1264241711.LODTYPES_DEPTH_ORPHANHD) &&
+ (d.CEntityDef.lodLevel != Unk_1264241711.LODTYPES_DEPTH_ORPHANHD)))
+ {
+ isroot = true;
+ p = null;
+ }
+ }
+
+ if (isroot)
+ {
+ roots.Add(d);
+ }
+ else
+ {
+ YmapEntityDef p = alldefs[pind];
+ p.AddChild(d);
+ }
+ }
+ for (int i = 0; i < alldefs.Count; i++)
+ {
+ alldefs[i].ChildListToArray();
+ }
+
+ AllEntities = alldefs.ToArray();
+ RootEntities = roots.ToArray();
+ if (mlodefs != null)
+ {
+ MloEntities = mlodefs.ToArray();
+ }
+
+
+ foreach (var ent in AllEntities)
+ {
+ ent.Extensions = MetaTypes.GetExtensions(Meta, ent.CEntityDef.extensions);
+ }
+ }
+
+ }
+
+ private void EnsureInstances(Meta Meta)
+ {
+ if (CMapData.instancedData.GrassInstanceList.Count1 != 0)
+ {
+ rage__fwGrassInstanceListDef[] batches = MetaTypes.ConvertDataArray(Meta, MetaName.rage__fwGrassInstanceListDef, CMapData.instancedData.GrassInstanceList);
+ YmapGrassInstanceBatch[] gbatches = new YmapGrassInstanceBatch[batches.Length];
+ for (int i = 0; i < batches.Length; i++)
+ {
+ var batch = batches[i];
+ rage__fwGrassInstanceListDef__InstanceData[] instdatas = MetaTypes.ConvertDataArray(Meta, MetaName.rage__fwGrassInstanceListDef__InstanceData, batch.InstanceList);
+ YmapGrassInstanceBatch gbatch = new YmapGrassInstanceBatch();
+ gbatch.Ymap = this;
+ gbatch.Batch = batch;
+ gbatch.Instances = instdatas;
+ gbatch.Position = (batch.BatchAABB.min.XYZ() + batch.BatchAABB.max.XYZ()) * 0.5f;
+ gbatch.Radius = (batch.BatchAABB.max.XYZ() - gbatch.Position).Length();
+ gbatch.AABBMin = (batch.BatchAABB.min.XYZ());
+ gbatch.AABBMax = (batch.BatchAABB.max.XYZ());
+ gbatches[i] = gbatch;
+ }
+ GrassInstanceBatches = gbatches;
+ }
+ if (CMapData.instancedData.PropInstanceList.Count1 != 0)
+ {
+ }
+ }
+
+ private void EnsureLodLights(Meta Meta)
+ {
+ //TODO!
+ if (CMapData.LODLightsSOA.direction.Count1 != 0)
+ {
+ }
+ }
+
+ private void EnsureDistantLODLights(Meta Meta)
+ {
+ if (CMapData.DistantLODLightsSOA.position.Count1 != 0)
+ {
+ DistantLODLights = new YmapDistantLODLights();
+ DistantLODLights.Ymap = this;
+ DistantLODLights.CDistantLODLight = CMapData.DistantLODLightsSOA;
+ DistantLODLights.colours = MetaTypes.GetUintArray(Meta, CMapData.DistantLODLightsSOA.RGBI);
+ DistantLODLights.positions = MetaTypes.ConvertDataArray(Meta, MetaName.VECTOR3, CMapData.DistantLODLightsSOA.position);
+ DistantLODLights.CalcBB();
+ }
+ }
+
+ private void EnsureTimeCycleModifiers(Meta Meta)
+ {
+ CTimeCycleModifiers = MetaTypes.ConvertDataArray(Meta, MetaName.CTimeCycleModifier, CMapData.timeCycleModifiers);
+ if (CTimeCycleModifiers != null)
+ {
+ TimeCycleModifiers = new YmapTimeCycleModifier[CTimeCycleModifiers.Length];
+ for (int i = 0; i < CTimeCycleModifiers.Length; i++)
+ {
+ YmapTimeCycleModifier tcm = new YmapTimeCycleModifier();
+ tcm.Ymap = this;
+ tcm.CTimeCycleModifier = CTimeCycleModifiers[i];
+ tcm.BBMin = tcm.CTimeCycleModifier.minExtents;
+ tcm.BBMax = tcm.CTimeCycleModifier.maxExtents;
+ TimeCycleModifiers[i] = tcm;
+ }
+ }
+ }
+
+ private void EnsureCarGens(Meta Meta)
+ {
+
+ CCarGens = MetaTypes.ConvertDataArray(Meta, MetaName.CCarGen, CMapData.carGenerators);
+ if (CCarGens != null)
+ {
+ //string str = MetaTypes.GetTypesInitString(resentry, Meta); //to generate structinfos and enuminfos
+ CarGenerators = new YmapCarGen[CCarGens.Length];
+ for (int i = 0; i < CCarGens.Length; i++)
+ {
+ CarGenerators[i] = new YmapCarGen(this, CCarGens[i]);
+ }
+ }
+ }
+
+ private void EnsureBoxOccluders(Meta meta)
+ {
+ CBoxOccluders = MetaTypes.ConvertDataArray(Meta, (MetaName)975711773, CMapData.boxOccluders);
+ if (CBoxOccluders != null)
+ {
+ BoxOccluders = new YmapBoxOccluder[CBoxOccluders.Length];
+ for (int i = 0; i < CBoxOccluders.Length; i++)
+ {
+ BoxOccluders[i] = new YmapBoxOccluder(this, CBoxOccluders[i]);
+ }
+ }
+ }
+
+ private void EnsureOccludeModels(Meta meta)
+ {
+ COccludeModels = MetaTypes.ConvertDataArray(Meta, (MetaName)2741784237, CMapData.occludeModels);
+ if (COccludeModels != null)
+ {
+ OccludeModels = new YmapOccludeModel[COccludeModels.Length];
+ for (int i = 0; i < COccludeModels.Length; i++)
+ {
+ OccludeModels[i] = new YmapOccludeModel(this, COccludeModels[i]);
+ }
+ }
+ }
+
+ private void EnsureContainerLods(Meta meta)
+ {
+
+ //TODO: containerLods
+ if (CMapData.containerLods.Count1 > 0)
+ {
+ //string str = MetaTypes.GetTypesInitString(Meta); //to generate structinfos and enuminfos
+
+
+ }
+
+ }
+
+
+ public void BuildCEntityDefs()
+ {
+ //recreates the CEntityDefs and CMloInstanceDefs arrays from AllEntities.
+ CEntityDefs = null;
+ CMloInstanceDefs = null;
+ if (AllEntities == null)
+ {
+ return;
+ }
+
+
+ List centdefs = new List();
+ List cmlodefs = new List();
+
+ for (int i = 0; i < AllEntities.Length; i++)
+ {
+ var ent = AllEntities[i];
+ if (ent.MloInstance != null)
+ {
+ cmlodefs.Add(ent.MloInstance.Instance);
+ }
+ else
+ {
+ centdefs.Add(ent.CEntityDef);
+ }
+ }
+
+ if (centdefs.Count > 0)
+ {
+ CEntityDefs = centdefs.ToArray();
+ }
+ if (cmlodefs.Count > 0)
+ {
+ CMloInstanceDefs = cmlodefs.ToArray();
+ }
+ }
+ public void BuildCCarGens()
+ {
+ //recreates the CCarGens array from CarGenerators.
+ if (CarGenerators == null)
+ {
+ CCarGens = null;
+ return;
+ }
+
+ int count = CarGenerators.Length;
+ CCarGens = new CCarGen[count];
+ for (int i = 0; i < count; i++)
+ {
+ CCarGens[i] = CarGenerators[i].CCarGen;
+ }
+ }
+
+ public void BuildInstances()
+ {
+ if (GrassInstanceBatches == null)
+ {
+ return;
+ }
+
+ if (PropInstanceBatches == null)
+ { }
+
+ int count = GrassInstanceBatches.Length;
+ for (int i = 0; i < count; i++)
+ {
+ var g = GrassInstanceBatches[i];
+ var b = g.Batch;
+
+ var aabb = new rage__spdAABB();
+ aabb.min = new Vector4(g.AABBMin, 0);
+ aabb.max = new Vector4(g.AABBMax, 0);
+
+ b.BatchAABB = aabb;
+
+ GrassInstanceBatches[i].Batch = b;
+ }
+ }
+
+ public byte[] Save()
+ {
+ //direct save to a raw, compressed ymap file (openIV-compatible format)
+
+
+ //since Ymap object contents have been modified, need to recreate the arrays which are what is saved.
+ BuildCEntityDefs(); //technically this isn't required anymore since the CEntityDefs is no longer used for saving.
+ BuildCCarGens();
+ BuildInstances();
+
+ //TODO:
+ //BuildLodLights();
+ //BuildDistantLodLights();
+ //BuildTimecycleModifiers(); //already being saved - update them..
+ //BuildBoxOccluders();
+ //BuildOccludeModels();
+ //BuildContainerLods();
+
+
+
+ MetaBuilder mb = new MetaBuilder();
+
+
+ var mdb = mb.EnsureBlock(MetaName.CMapData);
+
+ CMapData mapdata = CMapData;
+
+
+
+ if ((AllEntities != null) && (AllEntities.Length > 0))
+ {
+ for (int i = 0; i < AllEntities.Length; i++)
+ {
+ var ent = AllEntities[i]; //save the extensions first..
+ ent._CEntityDef.extensions = mb.AddWrapperArrayPtr(ent.Extensions);
+ }
+
+ MetaPOINTER[] ptrs = new MetaPOINTER[AllEntities.Length];
+ for (int i = 0; i < AllEntities.Length; i++)
+ {
+ var ent = AllEntities[i];
+ if (ent.MloInstance != null)
+ {
+ ptrs[i] = mb.AddItemPtr(MetaName.CMloInstanceDef, ent.MloInstance.Instance);
+ }
+ else
+ {
+ ptrs[i] = mb.AddItemPtr(MetaName.CEntityDef, ent.CEntityDef);
+ }
+ }
+ mapdata.entities = mb.AddPointerArray(ptrs);
+ }
+ else
+ {
+ mapdata.entities = new Array_StructurePointer();
+ }
+
+ mapdata.timeCycleModifiers = mb.AddItemArrayPtr(MetaName.CTimeCycleModifier, CTimeCycleModifiers);
+
+ mapdata.physicsDictionaries = mb.AddHashArrayPtr(physicsDictionaries);
+
+ mapdata.carGenerators = mb.AddItemArrayPtr(MetaName.CCarGen, CCarGens);
+
+ //clear everything out for now - TODO: fix
+ if (mapdata.containerLods.Count1 != 0) LogSaveWarning("containerLods were not saved. (TODO!)");
+ if (mapdata.occludeModels.Count1 != 0) LogSaveWarning("occludeModels were not saved. (TODO!)");
+ if (mapdata.boxOccluders.Count1 != 0) LogSaveWarning("boxOccluders were not saved. (TODO!)");
+ if (mapdata.instancedData.PropInstanceList.Count1 != 0) LogSaveWarning("instancedData.PropInstanceList was not saved. (TODO!)");
+ if (mapdata.LODLightsSOA.direction.Count1 != 0) LogSaveWarning("LODLightsSOA was not saved. (TODO!)");
+ if (mapdata.DistantLODLightsSOA.position.Count1 != 0) LogSaveWarning("DistantLODLightsSOA was not saved. (TODO!)");
+ mapdata.containerLods = new Array_Structure();
+ mapdata.occludeModels = new Array_Structure();
+ mapdata.boxOccluders = new Array_Structure();
+
+ if ((GrassInstanceBatches != null) && (GrassInstanceBatches.Length > 0))
+ {
+ var instancedData = new rage__fwInstancedMapData();
+ rage__fwGrassInstanceListDef[] batches = new rage__fwGrassInstanceListDef[GrassInstanceBatches.Length];
+ for (int i = 0; i < GrassInstanceBatches.Length; i++)
+ {
+ var batch = GrassInstanceBatches[i];
+
+ if (batch != null)
+ {
+ var b = batch.Batch;
+ b.InstanceList = mb.AddItemArrayPtr(MetaName.rage__fwGrassInstanceListDef__InstanceData, batch.Instances);
+ batches[i] = b;
+ }
+ }
+
+ instancedData.GrassInstanceList = mb.AddItemArrayPtr(MetaName.rage__fwGrassInstanceListDef, batches);
+ mapdata.instancedData = instancedData;
+ }
+ else
+ {
+ mapdata.instancedData = new rage__fwInstancedMapData();
+ }
+
+ mapdata.LODLightsSOA = new CLODLight();
+ mapdata.DistantLODLightsSOA = new CDistantLODLight();
+
+
+
+ var block = new CBlockDesc();
+ block.name = mb.AddStringPtr(Path.GetFileNameWithoutExtension(Name));
+ block.exportedBy = mb.AddStringPtr("CodeWalker");
+ block.time = mb.AddStringPtr(DateTime.UtcNow.ToString("dd MMMM yyyy HH:mm"));
+
+ mapdata.block = block;
+
+
+ string name = Path.GetFileNameWithoutExtension(Name);
+ uint nameHash = JenkHash.GenHash(name);
+ mapdata.name = new MetaHash(nameHash);//make sure name is upto date...
+
+
+ mb.AddItem(MetaName.CMapData, mapdata);
+
+
+
+ //make sure all the relevant structure and enum infos are present.
+ if ((GrassInstanceBatches != null) && (GrassInstanceBatches.Length > 0))
+ {
+ mb.AddStructureInfo(MetaName.rage__spdAABB);
+ mb.AddStructureInfo(MetaName.rage__fwGrassInstanceListDef__InstanceData);
+ mb.AddStructureInfo(MetaName.rage__fwGrassInstanceListDef);
+ }
+ mb.AddStructureInfo(MetaName.rage__fwInstancedMapData);
+ mb.AddStructureInfo(MetaName.CLODLight);
+ mb.AddStructureInfo(MetaName.CDistantLODLight);
+ mb.AddStructureInfo(MetaName.CBlockDesc);
+ mb.AddStructureInfo(MetaName.CMapData);
+ mb.AddStructureInfo(MetaName.CEntityDef);
+ mb.AddStructureInfo(MetaName.CMloInstanceDef);
+ mb.AddStructureInfo(MetaName.CTimeCycleModifier);
+ if ((CCarGens != null) && (CCarGens.Length > 0))
+ {
+ mb.AddStructureInfo(MetaName.CCarGen);
+ }
+
+ mb.AddEnumInfo((MetaName)1264241711); //LODTYPES_
+ mb.AddEnumInfo((MetaName)648413703); //PRI_
+
+
+ Meta meta = mb.GetMeta();
+
+ byte[] data = ResourceBuilder.Build(meta, 2); //ymap is version 2...
+
+
+ return data;
+ }
+
+ private void LogSaveWarning(string w)
+ {
+ if (SaveWarnings == null) SaveWarnings = new List();
+ SaveWarnings.Add(w);
+ }
+
+
+
+
+ public void EnsureChildYmaps(GameFileCache gfc)
+ {
+ if (ChildYmaps == null)
+ {
+ //no children here... look for child ymap....
+ var node = gfc.GetMapNode(RpfFileEntry.ShortNameHash);
+ if ((node != null) && (node.Children != null) && (node.Children.Length > 0))
+ {
+ ChildYmaps = new YmapFile[node.Children.Length];
+ for (int i = 0; i < ChildYmaps.Length; i++)
+ {
+ var chash = node.Children[i].Name;
+ ChildYmaps[i] = gfc.GetYmap(chash);
+ if (ChildYmaps[i] == null)
+ {
+ //couldn't find child ymap..
+ }
+ }
+ }
+ }
+
+
+ bool needupd = false;
+ if (ChildYmaps != null)
+ {
+ for (int i = 0; i < ChildYmaps.Length; i++)
+ {
+ var cmap = ChildYmaps[i];
+ if (cmap == null) continue; //nothing here..
+ if (!cmap.Loaded)
+ {
+ //ChildYmaps[i] = gfc.GetYmap(cmap.Hash); //incase no load was requested.
+ cmap = gfc.GetYmap(cmap.Key.Hash);
+ ChildYmaps[i] = cmap;
+ }
+ if ((cmap.Loaded) && (!cmap.MergedWithParent))
+ {
+ needupd = true;
+ }
+ }
+ }
+
+ if ((ChildYmaps != null) && needupd)
+ {
+ List newroots = new List(RootEntities);
+ for (int i = 0; i < ChildYmaps.Length; i++)
+ {
+ var cmap = ChildYmaps[i];
+ if (cmap == null) continue; //nothing here..
+ //cmap.EnsureChildYmaps();
+ if ((cmap.Loaded) && (!cmap.MergedWithParent))
+ {
+ cmap.MergedWithParent = true;
+ if (cmap.RootEntities != null)
+ {
+ foreach (var rcent in cmap.RootEntities)
+ {
+ int pind = rcent.CEntityDef.parentIndex;
+ if (pind < 0)
+ {
+ if (rcent.CEntityDef.lodLevel != Unk_1264241711.LODTYPES_DEPTH_ORPHANHD)
+ {
+ }
+ //pind = 0;
+ }
+ if ((pind >= 0) && (pind < AllEntities.Length))
+ {
+ var pentity = AllEntities[pind];
+ pentity.AddChild(rcent);
+ }
+ else
+ {
+ //TODO: fix this!!
+ //newroots.Add(rcent); //not sure this is the right approach.
+ //////rcent.Owner = this;
+ }
+ }
+ }
+ }
+ }
+ if (AllEntities != null)
+ {
+ for (int i = 0; i < AllEntities.Length; i++)
+ {
+ AllEntities[i].ChildListToMergedArray();
+ }
+ }
+
+ RootEntities = newroots.ToArray();
+ }
+
+
+ }
+
+
+
+
+ public void AddEntity(YmapEntityDef ent)
+ {
+ //used by the editor to add to the ymap.
+
+ List allents = new List();
+ if (AllEntities != null) allents.AddRange(AllEntities);
+ ent.Index = allents.Count;
+ ent.Ymap = this;
+ allents.Add(ent);
+ AllEntities = allents.ToArray();
+
+
+ if ((ent.Parent == null) || (ent.Parent.Ymap != this))
+ {
+ //root entity, add to roots.
+
+ List rootents = new List();
+ if (RootEntities != null) rootents.AddRange(RootEntities);
+ rootents.Add(ent);
+ RootEntities = rootents.ToArray();
+ }
+
+ HasChanged = true;
+ }
+
+ public bool RemoveEntity(YmapEntityDef ent)
+ {
+ //used by the editor to remove from the ymap.
+ if (ent == null) return false;
+
+ var res = true;
+
+ int idx = ent.Index;
+ List newAllEntities = new List();
+ List newRootEntities = new List();
+
+ for (int i = 0; i < AllEntities.Length; i++)
+ {
+ var oent = AllEntities[i];
+ oent.Index = newAllEntities.Count;
+ if (oent != ent) newAllEntities.Add(oent);
+ else if (i != idx)
+ {
+ res = false; //indexes didn't match.. this shouldn't happen!
+ }
+ }
+ for (int i = 0; i < RootEntities.Length; i++)
+ {
+ var oent = RootEntities[i];
+ if (oent != ent) newRootEntities.Add(oent);
+ }
+
+ if ((AllEntities.Length == newAllEntities.Count) || (RootEntities.Length == newRootEntities.Count))
+ {
+ res = false;
+ }
+
+ AllEntities = newAllEntities.ToArray();
+ RootEntities = newRootEntities.ToArray();
+
+ HasChanged = true;
+
+ return res;
+ }
+
+
+ public void AddCarGen(YmapCarGen cargen)
+ {
+ List cargens = new List();
+ if (CarGenerators != null) cargens.AddRange(CarGenerators);
+ cargen.Ymap = this;
+ cargens.Add(cargen);
+ CarGenerators = cargens.ToArray();
+
+ HasChanged = true;
+ }
+
+ public bool RemoveCarGen(YmapCarGen cargen)
+ {
+ if (cargen == null) return false;
+
+ List newcargens = new List();
+
+ if (CarGenerators != null)
+ {
+ for (int i = 0; i < CarGenerators.Length; i++)
+ {
+ var cg = CarGenerators[i];
+ if (cg != cargen)
+ {
+ newcargens.Add(cg);
+ }
+ }
+ if (newcargens.Count == CarGenerators.Length)
+ {
+ return false; //nothing removed... wasn't present?
+ }
+ }
+
+
+ CarGenerators = newcargens.ToArray();
+
+ HasChanged = true;
+
+ return true;
+ }
+
+
+ public void AddGrassBatch(YmapGrassInstanceBatch newbatch)
+ {
+ List batches = new List();
+ if (GrassInstanceBatches != null) batches.AddRange(GrassInstanceBatches);
+ newbatch.Ymap = this;
+ batches.Add(newbatch);
+ GrassInstanceBatches = batches.ToArray();
+
+ HasChanged = true;
+ }
+
+ public bool RemoveGrassBatch(YmapGrassInstanceBatch batch)
+ {
+ if (batch == null) return false;
+
+ List batches = new List();
+
+ if (GrassInstanceBatches != null)
+ {
+ for (int i = 0; i < GrassInstanceBatches.Length; i++)
+ {
+ var gb = GrassInstanceBatches[i];
+ if (gb != batch)
+ {
+ batches.Add(gb);
+ }
+ }
+ if (batches.Count == GrassInstanceBatches.Length)
+ {
+ return false; //nothing removed... wasn't present?
+ }
+ }
+
+
+ GrassInstanceBatches = batches.ToArray();
+
+ HasChanged = true;
+
+ return true;
+ }
+
+
+
+
+ public bool CalcFlags()
+ {
+ uint flags = 0;
+ uint contentFlags = 0;
+
+ if (AllEntities != null)
+ {
+ foreach (var yent in AllEntities)
+ {
+ var cent = yent.CEntityDef;
+ switch (cent.lodLevel)
+ {
+ case Unk_1264241711.LODTYPES_DEPTH_ORPHANHD:
+ case Unk_1264241711.LODTYPES_DEPTH_HD:
+ contentFlags = SetBit(contentFlags, 0); //1
+ break;
+ case Unk_1264241711.LODTYPES_DEPTH_LOD:
+ contentFlags = SetBit(contentFlags, 1); //2
+ flags = SetBit(flags, 1); //2
+ break;
+ case Unk_1264241711.LODTYPES_DEPTH_SLOD1:
+ contentFlags = SetBit(contentFlags, 4); //16
+ flags = SetBit(flags, 1); //2
+ break;
+ case Unk_1264241711.LODTYPES_DEPTH_SLOD2:
+ case Unk_1264241711.LODTYPES_DEPTH_SLOD3:
+ case Unk_1264241711.LODTYPES_DEPTH_SLOD4:
+ contentFlags = SetBit(contentFlags, 2); //4
+ contentFlags = SetBit(contentFlags, 4); //16
+ flags = SetBit(flags, 1); //2
+ break;
+ }
+ }
+ }
+
+ if ((CMloInstanceDefs != null) && (CMloInstanceDefs.Length > 0))
+ {
+ contentFlags = SetBit(contentFlags, 3); //8
+ }
+ if ((physicsDictionaries != null) && (physicsDictionaries.Length > 0))
+ {
+ contentFlags = SetBit(contentFlags, 6); //64
+ }
+ if ((GrassInstanceBatches != null) && (GrassInstanceBatches.Length > 0))
+ {
+ contentFlags = SetBit(contentFlags, 10); //64
+ }
+
+
+ bool change = false;
+ if (_CMapData.flags != flags)
+ {
+ _CMapData.flags = flags;
+ change = true;
+ }
+ if (_CMapData.contentFlags != contentFlags)
+ {
+ _CMapData.contentFlags = contentFlags;
+ change = true;
+ }
+ return change;
+ }
+
+ public bool CalcExtents()
+ {
+ Vector3 emin = new Vector3(float.MaxValue);
+ Vector3 emax = new Vector3(float.MinValue);
+ Vector3 smin = new Vector3(float.MaxValue);
+ Vector3 smax = new Vector3(float.MinValue);
+ Vector3[] c = new Vector3[8];
+ Vector3[] s = new Vector3[8];
+
+ if (AllEntities != null)
+ {
+ for (int i = 0; i < AllEntities.Length; i++)
+ {
+ var ent = AllEntities[i];
+ var arch = ent.Archetype;
+ var ori = ent.Orientation;
+ float loddist = ent._CEntityDef.lodDist;
+
+ Vector3 bbmin = ent.Position - ent.BSRadius; //sphere
+ Vector3 bbmax = ent.Position + ent.BSRadius;
+ Vector3 sbmin = bbmin - loddist;
+ Vector3 sbmax = bbmax + loddist;
+ if (arch != null)
+ {
+ if (loddist <= 0.0f)
+ {
+ loddist = arch.LodDist;
+ }
+
+ Vector3 abmin = arch.BBMin * ent.Scale; //entity box
+ Vector3 abmax = arch.BBMax * ent.Scale;
+ c[0] = abmin;
+ c[1] = new Vector3(abmin.X, abmin.Y, abmax.Z);
+ c[2] = new Vector3(abmin.X, abmax.Y, abmin.Z);
+ c[3] = new Vector3(abmin.X, abmax.Y, abmax.Z);
+ c[4] = new Vector3(abmax.X, abmin.Y, abmin.Z);
+ c[5] = new Vector3(abmax.X, abmin.Y, abmax.Z);
+ c[6] = new Vector3(abmax.X, abmax.Y, abmin.Z);
+ c[7] = abmax;
+
+ abmin = arch.BBMin * ent.Scale - loddist; //streaming box
+ abmax = arch.BBMax * ent.Scale + loddist;
+ s[0] = abmin;
+ s[1] = new Vector3(abmin.X, abmin.Y, abmax.Z);
+ s[2] = new Vector3(abmin.X, abmax.Y, abmin.Z);
+ s[3] = new Vector3(abmin.X, abmax.Y, abmax.Z);
+ s[4] = new Vector3(abmax.X, abmin.Y, abmin.Z);
+ s[5] = new Vector3(abmax.X, abmin.Y, abmax.Z);
+ s[6] = new Vector3(abmax.X, abmax.Y, abmin.Z);
+ s[7] = abmax;
+
+ bbmin = new Vector3(float.MaxValue);
+ bbmax = new Vector3(float.MinValue);
+ sbmin = new Vector3(float.MaxValue);
+ sbmax = new Vector3(float.MinValue);
+ for (int j = 0; j < 8; j++)
+ {
+ Vector3 corn = ori.Multiply(c[j]) + ent.Position;
+ bbmin = Vector3.Min(bbmin, corn);
+ bbmax = Vector3.Max(bbmax, corn);
+
+ corn = ori.Multiply(s[j]) + ent.Position;
+ sbmin = Vector3.Min(sbmin, corn);
+ sbmax = Vector3.Max(sbmax, corn);
+ }
+ }
+
+ emin = Vector3.Min(emin, bbmin);
+ emax = Vector3.Max(emax, bbmax);
+
+ smin = Vector3.Min(smin, sbmin);
+ smax = Vector3.Max(smax, sbmax);
+ }
+ }
+
+ if (GrassInstanceBatches != null)
+ {
+ //var lodoffset = Vector3.Zero;// new Vector3(0, 0, 100); //IDK WHY -neos7 //dexy: i guess it's not completely necessary... //blame neos
+ foreach (var batch in GrassInstanceBatches) //thanks to Neos7
+ {
+ emin = Vector3.Min(emin, batch.AABBMin);
+ emax = Vector3.Max(emax, batch.AABBMax);
+
+ smin = Vector3.Min(smin, (batch.AABBMin - batch.Batch.lodDist)); // + lodoffset
+ smax = Vector3.Max(smax, (batch.AABBMax + batch.Batch.lodDist)); // - lodoffset
+ }
+ }
+
+
+ bool change = false;
+ if (_CMapData.entitiesExtentsMin != emin)
+ {
+ _CMapData.entitiesExtentsMin = emin;
+ change = true;
+ }
+ if (_CMapData.entitiesExtentsMax != emax)
+ {
+ _CMapData.entitiesExtentsMax = emax;
+ change = true;
+ }
+ if (_CMapData.streamingExtentsMin != smin)
+ {
+ _CMapData.streamingExtentsMin = smin;
+ change = true;
+ }
+ if (_CMapData.streamingExtentsMax != smax)
+ {
+ _CMapData.streamingExtentsMax = smax;
+ change = true;
+ }
+ return change;
+ }
+
+
+
+ private static uint SetBit(uint value, int bit)
+ {
+ return (value | (1u << bit));
+ }
+
+ }
+
+
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class YmapEntityDef
+ {
+ public Archetype Archetype { get; set; } //cached by GameFileCache on loading...
+ public Vector3 BBMin;//oriented archetype AABBmin
+ public Vector3 BBMax;//oriented archetype AABBmax
+ public Vector3 BSCenter; //oriented archetype BS center
+ public float BSRadius;//cached from archetype
+
+ public CEntityDef _CEntityDef;
+ public CEntityDef CEntityDef { get { return _CEntityDef; } set { _CEntityDef = value; } }
+ private List ChildList { get; set; }
+ public YmapEntityDef[] Children { get; set; }
+ public YmapEntityDef[] ChildrenMerged;// { get; set; }
+ public Vector3 Position { get; set; }
+ public Quaternion Orientation { get; set; }
+ public Vector3 Scale { get; set; }
+ public bool IsMlo { get; set; }
+ public MloInstanceData MloInstance { get; set; }
+ public YmapEntityDef MloParent { get; set; }
+ public MCMloEntitySet MloEntitySet { get; set; }
+ public Vector3 MloRefPosition { get; set; }
+ public Quaternion MloRefOrientation { get; set; }
+ public MetaWrapper[] Extensions { get; set; }
+
+ public int Index { get; set; }
+ public bool IsVisible; //used for rendering
+ public bool ChildrenVisible; //used for rendering
+ public bool ChildrenRendered; //used when rendering ymap mode to reduce LOD flashing...
+ public YmapEntityDef Parent { get; set; } //for browsing convenience, also used/updated for rendering
+ public MetaHash ParentName { get; set; } //just for browsing convenience
+
+ public YmapFile Ymap { get; set; }
+
+ public Vector3 PivotPosition = Vector3.Zero;
+ public Quaternion PivotOrientation = Quaternion.Identity;
+ public Vector3 WidgetPosition = Vector3.Zero;
+ public Quaternion WidgetOrientation = Quaternion.Identity;
+
+
+ public string Name
+ {
+ get
+ {
+ return _CEntityDef.archetypeName.ToString();
+ }
+ }
+
+
+ public YmapEntityDef()
+ {
+ Scale = Vector3.One;
+ Position = Vector3.One;
+ Orientation = Quaternion.Identity;
+ }
+ public YmapEntityDef(YmapFile ymap, int index, ref CEntityDef def)
+ {
+ Ymap = ymap;
+ Index = index;
+ CEntityDef = def;
+ Scale = new Vector3(new Vector2(CEntityDef.scaleXY), CEntityDef.scaleZ);
+ Position = CEntityDef.position;
+ Orientation = new Quaternion(CEntityDef.rotation);
+ if (Orientation != Quaternion.Identity)
+ {
+ Orientation = Quaternion.Invert(Orientation);
+ }
+ IsMlo = false;
+
+ UpdateWidgetPosition();
+ UpdateWidgetOrientation();
+ }
+
+ public YmapEntityDef(YmapFile ymap, int index, ref CMloInstanceDef mlo)
+ {
+ Ymap = ymap;
+ Index = index;
+ CEntityDef = mlo.CEntityDef;
+ Scale = new Vector3(new Vector2(CEntityDef.scaleXY), CEntityDef.scaleZ);
+ Position = CEntityDef.position;
+ Orientation = new Quaternion(CEntityDef.rotation);
+ //if (Orientation != Quaternion.Identity)
+ //{
+ // Orientation = Quaternion.Invert(Orientation);
+ //}
+ IsMlo = true;
+
+ MloInstance = new MloInstanceData();
+ MloInstance.Instance = mlo;
+
+ UpdateWidgetPosition();
+ UpdateWidgetOrientation();
+ }
+
+
+ public void SetArchetype(Archetype arch)
+ {
+ Archetype = arch;
+ if (Archetype != null)
+ {
+ float smax = Math.Max(Scale.X, Scale.Z);
+ BSRadius = Archetype.BSRadius * smax;
+ BSCenter = Orientation.Multiply(Archetype.BSCenter) * Scale;
+ if (Orientation == Quaternion.Identity)
+ {
+ BBMin = (Archetype.BBMin * Scale) + Position;
+ BBMax = (Archetype.BBMax * Scale) + Position;
+ }
+ else
+ {
+ BBMin = Position - BSRadius;
+ BBMax = Position + BSRadius;
+ ////not ideal: should transform all 8 corners!
+ }
+
+ if (Archetype.Type == MetaName.CMloArchetypeDef)
+ {
+ //transform interior entities into world space...
+ var mloa = Archetype as MloArchetype;
+ if (MloInstance == null)
+ {
+ MloInstance = new MloInstanceData();
+ }
+ MloInstance.CreateYmapEntities(this, mloa);
+
+ if (BSRadius == 0.0f)
+ {
+ BSRadius = CEntityDef.lodDist;//need something so it doesn't get culled...
+ }
+ }
+
+ }
+
+ }
+
+ public void SetPosition(Vector3 pos)
+ {
+ if (MloParent != null)
+ {
+ //TODO: SetPosition for interior entities!
+ Position = pos;
+ var inst = MloParent.MloInstance;
+ if (inst != null)
+ {
+ //transform world position into mlo space
+ //MloRefPosition = ...
+ //MloRefOrientation = ...
+ }
+ }
+ else
+ {
+ Position = pos;
+ _CEntityDef.position = pos;
+
+ if (Archetype != null)
+ {
+ BSCenter = Orientation.Multiply(Archetype.BSCenter) * Scale;
+ }
+ if ((Archetype != null) && (Orientation == Quaternion.Identity))
+ {
+ BBMin = (Archetype.BBMin * Scale) + Position;
+ BBMax = (Archetype.BBMax * Scale) + Position;
+ }
+ else
+ {
+ BBMin = Position - (BSRadius);
+ BBMax = Position + (BSRadius);
+ ////not ideal: should transform all 8 corners!
+ }
+
+
+
+ UpdateWidgetPosition();
+ }
+
+
+ if (MloInstance != null)
+ {
+ MloInstance.SetPosition(Position);
+ MloInstance.UpdateEntities();
+ }
+
+ }
+
+ public void SetOrientation(Quaternion ori)
+ {
+ Quaternion inv = Quaternion.Normalize(Quaternion.Invert(ori));
+ Orientation = ori;
+ _CEntityDef.rotation = new Vector4(inv.X, inv.Y, inv.Z, inv.W);
+
+ if (MloInstance != null)
+ {
+ MloInstance.SetOrientation(ori);
+ }
+
+
+ if (Archetype != null)
+ {
+ BSCenter = Orientation.Multiply(Archetype.BSCenter) * Scale;
+ }
+
+ UpdateWidgetPosition();
+ UpdateWidgetOrientation();
+ }
+ public void SetOrientationInv(Quaternion inv)
+ {
+ Quaternion ori = Quaternion.Normalize(Quaternion.Invert(inv));
+ Orientation = ori;
+ _CEntityDef.rotation = new Vector4(inv.X, inv.Y, inv.Z, inv.W);
+
+ if (MloInstance != null)
+ {
+ MloInstance.SetOrientation(ori);
+ }
+
+
+ if (Archetype != null)
+ {
+ BSCenter = Orientation.Multiply(Archetype.BSCenter) * Scale;
+ }
+
+ UpdateWidgetPosition();
+ UpdateWidgetOrientation();
+ }
+
+ public void SetScale(Vector3 s)
+ {
+ Scale = new Vector3(s.X, s.X, s.Z);
+ _CEntityDef.scaleXY = s.X;
+ _CEntityDef.scaleZ = s.Z;
+ if (Archetype != null)
+ {
+ float smax = Math.Max(Scale.X, Scale.Z);
+ BSRadius = Archetype.BSRadius * smax;
+ }
+ SetPosition(Position);//update the BB
+ }
+
+
+
+ public void SetPivotPosition(Vector3 pos)
+ {
+ PivotPosition = pos;
+
+ UpdateWidgetPosition();
+ }
+
+ public void SetPivotOrientation(Quaternion ori)
+ {
+ PivotOrientation = ori;
+
+ UpdateWidgetOrientation();
+ }
+
+
+ public void SetPositionFromWidget(Vector3 pos)
+ {
+ SetPosition(pos - Orientation.Multiply(PivotPosition));
+ }
+ public void SetOrientationFromWidget(Quaternion ori)
+ {
+ var newori = Quaternion.Normalize(Quaternion.Multiply(ori, Quaternion.Invert(PivotOrientation)));
+ var newpos = WidgetPosition - newori.Multiply(PivotPosition);
+ SetOrientation(newori);
+ SetPosition(newpos);
+ }
+ public void SetPivotPositionFromWidget(Vector3 pos)
+ {
+ var orinv = Quaternion.Invert(Orientation);
+ SetPivotPosition(orinv.Multiply(pos - Position));
+ }
+ public void SetPivotOrientationFromWidget(Quaternion ori)
+ {
+ var orinv = Quaternion.Invert(Orientation);
+ SetPivotOrientation(Quaternion.Multiply(orinv, ori));
+ }
+
+
+ public void UpdateWidgetPosition()
+ {
+ WidgetPosition = Position + Orientation.Multiply(PivotPosition);
+ }
+ public void UpdateWidgetOrientation()
+ {
+ WidgetOrientation = Quaternion.Multiply(Orientation, PivotOrientation);
+ }
+
+
+ public void AddChild(YmapEntityDef c)
+ {
+ if (ChildList == null)
+ {
+ ChildList = new List();
+ }
+ c.Parent = this;
+ c.ParentName = CEntityDef.archetypeName;
+
+ ChildList.Add(c);
+ }
+
+ public void ChildListToArray()
+ {
+ if (ChildList == null) return;
+ //if (Children == null)
+ //{
+ Children = ChildList.ToArray();
+ ChildrenMerged = Children;//include these by default in merged array
+ //}
+ //else
+ //{
+ // List newc = new List(Children.Length + ChildList.Count);
+ // newc.AddRange(Children);
+ // newc.AddRange(ChildList);
+ // Children = newc.ToArray();
+ //}
+ ChildList.Clear();
+ ChildList = null;
+ }
+ public void ChildListToMergedArray()
+ {
+ if (ChildList == null) return;
+ if (ChildrenMerged == null)
+ {
+ ChildrenMerged = ChildList.ToArray();
+ }
+ else
+ {
+ List newc = new List(ChildrenMerged.Length + ChildList.Count);
+ newc.AddRange(ChildrenMerged);
+ newc.AddRange(ChildList);
+ ChildrenMerged = newc.ToArray();
+ }
+ ChildList.Clear();
+ ChildList = null;
+ }
+
+ public override string ToString()
+ {
+ return CEntityDef.ToString() + ((ChildList != null) ? (" (" + ChildList.Count.ToString() + " children) ") : " ") + CEntityDef.lodLevel.ToString();
+ }
+
+ }
+
+
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class YmapGrassInstanceBatch
+ {
+ public Archetype Archetype { get; set; } //cached by GameFileCache on loading...
+ public rage__fwGrassInstanceListDef Batch { get; set; }
+ public rage__fwGrassInstanceListDef__InstanceData[] Instances { get; set; }
+ public Vector3 Position { get; set; } //calculated from AABB
+ public float Radius { get; set; } //calculated from AABB
+ public Vector3 AABBMin { get; set; }
+ public Vector3 AABBMax { get; set; }
+ public Vector3 CamRel; //used for rendering...
+ public float Distance; //used for rendering
+ public YmapFile Ymap { get; set; }
+
+ public override string ToString()
+ {
+ return Batch.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class YmapPropInstanceBatch
+ {
+ public YmapFile Ymap { get; set; }
+
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class YmapDistantLODLights
+ {
+ public CDistantLODLight CDistantLODLight { get; set; }
+ public uint[] colours { get; set; }
+ public MetaVECTOR3[] positions { get; set; }
+
+ public Vector3 BBMin { get; set; }
+ public Vector3 BBMax { get; set; }
+ public YmapFile Ymap { get; set; }
+
+ public void CalcBB()
+ {
+ if (positions != null)
+ {
+ Vector3 min = new Vector3(float.MaxValue);
+ Vector3 max = new Vector3(float.MinValue);
+ for (int i = 0; i < positions.Length; i++)
+ {
+ var p = positions[i];
+ Vector3 pv = p.ToVector3();
+ min = Vector3.Min(min, pv);
+ max = Vector3.Max(max, pv);
+ }
+ BBMin = min;
+ BBMax = max;
+ }
+
+ }
+ }
+
+
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class YmapTimeCycleModifier
+ {
+ public CTimeCycleModifier CTimeCycleModifier { get; set; }
+ public World.TimecycleMod TimeCycleModData { get; set; }
+
+ public Vector3 BBMin { get; set; }
+ public Vector3 BBMax { get; set; }
+
+ public YmapFile Ymap { get; set; }
+ }
+
+
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class YmapCarGen
+ {
+ public CCarGen _CCarGen;
+ public CCarGen CCarGen { get { return _CCarGen; } set { _CCarGen = value; } }
+
+ public Vector3 Position { get; set; }
+ public Quaternion Orientation { get; set; }
+ public Vector3 BBMin { get; set; }
+ public Vector3 BBMax { get; set; }
+
+ public YmapFile Ymap { get; set; }
+
+
+ public YmapCarGen(YmapFile ymap, CCarGen cargen)
+ {
+ float hlen = cargen.perpendicularLength * 0.5f;
+ Ymap = ymap;
+ CCarGen = cargen;
+ Position = cargen.position;
+ CalcOrientation();
+ BBMin = new Vector3(-hlen);
+ BBMax = new Vector3(hlen);
+ }
+
+
+ public void CalcOrientation()
+ {
+ float angl = (float)Math.Atan2(_CCarGen.orientY, _CCarGen.orientX);
+ Orientation = Quaternion.RotationYawPitchRoll(0.0f, 0.0f, angl);
+ }
+
+ public void SetPosition(Vector3 pos)
+ {
+ Position = pos;
+ _CCarGen.position = pos;
+ }
+ public void SetOrientation(Quaternion ori)
+ {
+ Orientation = ori;
+
+ float len = Math.Max(_CCarGen.perpendicularLength * 1.5f, 5.0f);
+ Vector3 v = new Vector3(len, 0, 0);
+ Vector3 t = ori.Multiply(v);
+
+ _CCarGen.orientX = t.X;
+ _CCarGen.orientY = t.Y;
+ }
+ public void SetScale(Vector3 scale)
+ {
+ float s = scale.X;
+ float hlen = s * 0.5f;
+ _CCarGen.perpendicularLength = s;
+
+ BBMin = new Vector3(-hlen);
+ BBMax = new Vector3(hlen);
+ }
+
+ public void SetLength(float length)
+ {
+ _CCarGen.perpendicularLength = length;
+ float hlen = length * 0.5f;
+
+ BBMin = new Vector3(-hlen);
+ BBMax = new Vector3(hlen);
+ }
+
+ public string NameString()
+ {
+ MetaHash mh = _CCarGen.carModel;
+ if ((mh == 0) && (_CCarGen.popGroup != 0))
+ {
+ mh = _CCarGen.popGroup;
+ }
+ return mh.ToString();
+ }
+
+ public override string ToString()
+ {
+ return _CCarGen.carModel.ToString() + ", " + Position.ToString() + ", " + _CCarGen.popGroup.ToString() + ", " + _CCarGen.livery.ToString();
+ }
+ }
+
+
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class YmapOccludeModel
+ {
+ public Unk_2741784237 _OccludeModel;
+ public Unk_2741784237 OccludeModel { get { return _OccludeModel; } set { _OccludeModel = value; } }
+
+ public YmapFile Ymap { get; set; }
+
+
+ public YmapOccludeModel(YmapFile ymap, Unk_2741784237 model)
+ {
+ Ymap = ymap;
+ _OccludeModel = model;
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class YmapBoxOccluder
+ {
+ public Unk_975711773 _Box;
+ public Unk_975711773 Box { get { return _Box; } set { _Box = value; } }
+
+ public YmapFile Ymap { get; set; }
+
+ public YmapBoxOccluder(YmapFile ymap, Unk_975711773 box)
+ {
+ Ymap = ymap;
+ _Box = box;
+ }
+
+ }
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YmfFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YmfFile.cs
new file mode 100644
index 0000000..1083bd7
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YmfFile.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ public class YmfFile : PackedFile
+ {
+ public RpfFileEntry FileEntry { get; set; }
+
+ public Meta Meta { get; set; }
+ public PsoFile Pso { get; set; }
+ public RbfFile Rbf { get; set; }
+
+ public YmfMapDataGroup[] MapDataGroups { get; set; }
+ public CImapDependency[] imapDependencies { get; set; }
+ public YmfImapDependency2[] imapDependencies2 { get; set; }
+ public YmfItypDependency2[] itypDependencies2 { get; set; }
+ public CHDTxdAssetBinding[] HDTxdAssetBindings { get; set; }
+ public YmfInterior[] Interiors { get; set; }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ FileEntry = entry;
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ MemoryStream ms = new MemoryStream(data);
+ if (RbfFile.IsRBF(ms))
+ {
+ Rbf = new RbfFile();
+ Rbf.Load(ms);
+
+ //x64j.rpf\\levels\\gta5\\_citye\\indust_01\\id1_props.rpf\\_manifest.ymf
+ //x64j.rpf\\levels\\gta5\\_citye\\indust_02\\id2_props.rpf\\_manifest.ymf
+ //x64q.rpf\\levels\\gta5\\_hills\\country_01\\cs1_railwyc.rpf\\_manifest.ymf
+ //all just HDTxd bindings
+
+ return;
+ }
+ if (PsoFile.IsPSO(ms))
+ {
+ Pso = new PsoFile();
+ Pso.Load(ms);
+
+ //PsoTypes.EnsurePsoTypes(Pso);
+
+ ProcessPSO();
+
+ return;
+ }
+ else
+ {
+
+ }
+ return;
+ }
+
+
+
+
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+ Meta = rd.ReadBlock();
+
+
+
+
+
+ }
+
+
+ private void ProcessPSO()
+ {
+
+ //See x64m.rpf\levels\gta5\_cityw\venice_01\venice_metadata.rpf\_manifest.ymf
+ //for TIMED YMAP stuff!!!!
+ //check CMapDataGroup.HoursOnOff
+
+
+ var d = PsoTypes.GetRootItem(Pso);
+
+ MapDataGroups = PsoTypes.GetObjectArray(Pso, d.MapDataGroups);
+
+ imapDependencies = PsoTypes.GetItemArray(Pso, d.imapDependencies);
+
+ imapDependencies2 = PsoTypes.GetObjectArray(Pso, d.imapDependencies_2);
+
+ itypDependencies2 = PsoTypes.GetObjectArray(Pso, d.itypDependencies_2);
+
+ HDTxdAssetBindings = PsoTypes.GetItemArray(Pso, d.HDTxdBindingArray);
+
+ Interiors = PsoTypes.GetObjectArray(Pso, d.Interiors);
+
+
+ }
+
+
+ public override string ToString()
+ {
+ return (FileEntry != null) ? FileEntry.Path : string.Empty;
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YmfMapDataGroup : PsoClass
+ {
+ public CMapDataGroup DataGroup { get; set; } //ymap name
+ public MetaHash[] Bounds { get; set; }
+ public MetaHash[] WeatherTypes { get; set; }
+ public MetaHash Name { get; set; }
+ public ushort Flags { get; set; }
+ public uint HoursOnOff { get; set; }
+
+ public override string ToString()
+ {
+ return DataGroup.ToString();
+ }
+
+ public override void Init(PsoFile pso, ref CMapDataGroup v)
+ {
+ DataGroup = v;
+ Bounds = PsoTypes.GetHashArray(pso, v.Bounds);
+ WeatherTypes = PsoTypes.GetHashArray(pso, v.WeatherTypes);
+ Name = v.Name;
+ Flags = v.Flags;
+ HoursOnOff = v.HoursOnOff;
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YmfImapDependency2 : PsoClass
+ {
+ public CImapDependencies Dep { get; set; }
+ public MetaHash[] itypDepArray { get; set; }//ybn hashes?
+
+ public override void Init(PsoFile pso, ref CImapDependencies v)
+ {
+ Dep = v;
+ itypDepArray = PsoTypes.GetHashArray(pso, v.itypDepArray);
+ }
+
+ public override string ToString()
+ {
+ return Dep.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YmfItypDependency2 : PsoClass
+ {
+ public CItypDependencies Dep { get; set; }
+ public MetaHash[] itypDepArray { get; set; }//ytyp hashes?
+
+ public override void Init(PsoFile pso, ref CItypDependencies v)
+ {
+ Dep = v;
+ itypDepArray = PsoTypes.GetHashArray(pso, v.itypDepArray);
+ }
+
+ public override string ToString()
+ {
+ return Dep.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YmfInterior : PsoClass
+ {
+ public Unk_741495440 Interior { get; set; }
+ public MetaHash[] Bounds { get; set; }//ybn hashes?
+
+ public override string ToString()
+ {
+ return Interior.ToString();
+ }
+
+ public override void Init(PsoFile pso, ref Unk_741495440 v)
+ {
+ Interior = v;
+ Bounds = PsoTypes.GetHashArray(pso, v.Bounds);
+ }
+ }
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YmtFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YmtFile.cs
new file mode 100644
index 0000000..0e52c51
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YmtFile.cs
@@ -0,0 +1,324 @@
+using CodeWalker.World;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YmtFile : GameFile, PackedFile
+ {
+
+ public Meta Meta { get; set; }
+ public PsoFile Pso { get; set; }
+ public RbfFile Rbf { get; set; }
+
+ public YmtFileFormat FileFormat { get; set; } = YmtFileFormat.Unknown;
+ public YmtFileContentType ContentType { get; set; } = YmtFileContentType.None;
+
+
+ public Dictionary CMapParentTxds { get; set; }
+
+ public YmtScenarioPointManifest CScenarioPointManifest { get; set; }
+
+ public MCScenarioPointRegion CScenarioPointRegion { get; set; }
+ public ScenarioRegion ScenarioRegion { get; set; }
+
+
+
+
+ //fields used by the editor:
+ public bool HasChanged { get; set; } = false;
+ public List SaveWarnings = null;
+
+ public YmtFile() : base(null, GameFileType.Ymt)
+ {
+ }
+ public YmtFile(RpfFileEntry entry) : base(entry, GameFileType.Ymt)
+ {
+ }
+
+
+
+ public void Load(byte[] data)
+ {
+ //direct load from a raw, compressed ymt resource file (openIV-compatible format)
+
+ RpfFile.LoadResourceFile(this, data, 2);
+
+ Loaded = true;
+ }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ RpfFileEntry = entry;
+ Name = entry.Name;
+ FilePath = Name;
+
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry != null)
+ {
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+ Meta = rd.ReadBlock();
+
+ var rootblock = Meta.GetRootBlock();
+ if (rootblock != null)
+ {
+ if (rootblock.StructureNameHash == MetaName.CScenarioPointRegion)
+ {
+ LoadScenarioPointRegion(Meta, rootblock);
+ }
+ }
+
+ Loaded = true;
+ return;
+ }
+
+
+ MemoryStream ms = new MemoryStream(data);
+
+ if (RbfFile.IsRBF(ms))
+ {
+ Rbf = new RbfFile();
+ var rbfstruct = Rbf.Load(ms);
+
+ if (rbfstruct.Name == "CMapParentTxds")
+ {
+ LoadMapParentTxds(rbfstruct);
+ }
+
+
+
+ Loaded = true;
+ return;
+ }
+ if (PsoFile.IsPSO(ms))
+ {
+ Pso = new PsoFile();
+ Pso.Load(ms);
+
+ //PsoTypes.EnsurePsoTypes(Pso);
+
+ var root = PsoTypes.GetRootEntry(Pso);
+ if (root != null)
+ {
+ if (root.NameHash == MetaName.CScenarioPointManifest)
+ {
+ LoadScenarioPointManifest(Pso);
+ }
+ }
+
+
+ Loaded = true;
+ return;
+ }
+ else
+ {
+
+ }
+
+
+
+
+ }
+
+
+ private void LoadMapParentTxds(RbfStructure rbfstruct)
+ {
+ FileFormat = YmtFileFormat.RBF;
+ ContentType = YmtFileContentType.MapParentTxds;
+
+ CMapParentTxds = new Dictionary();
+ //StringBuilder sblist = new StringBuilder();
+ foreach(var child in rbfstruct.Children)
+ {
+ var childstruct = child as RbfStructure;
+ if ((childstruct != null) && (childstruct.Name == "txdRelationships"))
+ {
+ foreach (var txdrel in childstruct.Children)
+ {
+ var txdrelstruct = txdrel as RbfStructure;
+ if ((txdrelstruct != null) && (txdrelstruct.Name == "item"))
+ {
+ string parentstr = string.Empty;
+ string childstr = string.Empty;
+ foreach(var item in txdrelstruct.Children)
+ {
+ var itemstruct = item as RbfStructure;
+ if ((itemstruct != null))
+ {
+ var strbytes = itemstruct.Children[0] as RbfBytes;
+ string thisstr = string.Empty;
+ if (strbytes != null)
+ {
+ thisstr = Encoding.ASCII.GetString(strbytes.Value).Replace("\0", "");
+ }
+ switch (item.Name)
+ {
+ case "parent":
+ parentstr = thisstr;
+ break;
+ case "child":
+ childstr = thisstr;
+ break;
+ }
+ }
+
+ }
+ if((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
+ {
+ if (!CMapParentTxds.ContainsKey(childstr))
+ {
+ CMapParentTxds.Add(childstr, parentstr);
+ }
+ else
+ {
+ }
+ //sblist.AppendLine(childstr + ": " + parentstr);
+ }
+ }
+ }
+ }
+ }
+ //string alltxdmap = sblist.ToString();
+ //if (!string.IsNullOrEmpty(alltxdmap))
+ //{
+ //}
+
+ }
+
+ private void LoadScenarioPointManifest(PsoFile pso)
+ {
+ FileFormat = YmtFileFormat.PSO;
+ ContentType = YmtFileContentType.ScenarioPointManifest;
+
+ CScenarioPointManifest = new YmtScenarioPointManifest();
+ CScenarioPointManifest.Load(pso);
+
+ }
+
+ private void LoadScenarioPointRegion(Meta meta, MetaDataBlock rootblock)
+ {
+ FileFormat = YmtFileFormat.RSC;
+ ContentType = YmtFileContentType.ScenarioPointRegion;
+
+ var cdata = MetaTypes.ConvertData(rootblock.Data);
+
+ CScenarioPointRegion = new MCScenarioPointRegion();
+ CScenarioPointRegion.Ymt = this;
+ CScenarioPointRegion.Load(meta, cdata);
+
+
+ ScenarioRegion = new ScenarioRegion();
+ ScenarioRegion.Load(this);
+
+ //string stypes = MetaTypes.GetTypesInitString(meta);
+ //if (!string.IsNullOrEmpty(stypes))
+ //{ }
+ }
+
+
+
+
+ public byte[] Save()
+ {
+
+ switch (ContentType)
+ {
+ case YmtFileContentType.MapParentTxds: return SaveMapParentTxds();
+ case YmtFileContentType.ScenarioPointManifest: return SaveScenarioPointManifest();
+ case YmtFileContentType.ScenarioPointRegion: return SaveScenarioPointRegion();
+ }
+
+ return null;
+ }
+
+
+ private byte[] SaveMapParentTxds()
+ {
+ return null;
+ }
+
+ private byte[] SaveScenarioPointManifest()
+ {
+ return null;
+ }
+
+ private byte[] SaveScenarioPointRegion()
+ {
+ if (ScenarioRegion != null)
+ {
+ return ScenarioRegion.Save();
+ }
+ return null;
+ }
+
+
+
+
+
+
+
+
+ private void LogSaveWarning(string w)
+ {
+ if (SaveWarnings == null) SaveWarnings = new List();
+ SaveWarnings.Add(w);
+ }
+
+
+
+
+
+
+ public override string ToString()
+ {
+ return RpfFileEntry.ToString();
+ }
+ }
+
+
+ public enum YmtFileFormat
+ {
+ Unknown = 0,
+ RSC = 1,
+ PSO = 2,
+ RBF = 3,
+ }
+ public enum YmtFileContentType
+ {
+ None = 0,
+ MapParentTxds = 1,
+ ScenarioPointManifest = 2,
+ ScenarioPointRegion = 3,
+ }
+
+
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YmtScenarioPointManifest
+ {
+ public CScenarioPointManifest _Data;
+ public CScenarioPointManifest Data { get { return _Data; } set { _Data = value; } }
+
+ public CScenarioPointRegionDef[] RegionDefs { get; set; }
+ public CScenarioPointGroup[] Groups { get; set; }
+ public MetaHash[] InteriorNames { get; set; }
+
+
+ public void Load(PsoFile pso)
+ {
+ Data = PsoTypes.GetRootItem(pso);
+ RegionDefs = PsoTypes.ConvertDataArray(pso, _Data.RegionDefs);
+ Groups = PsoTypes.ConvertDataArray(pso, _Data.Groups);
+ InteriorNames = PsoTypes.GetHashArray(pso, _Data.InteriorNames);
+ }
+
+ }
+
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YndFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YndFile.cs
new file mode 100644
index 0000000..e862631
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YndFile.cs
@@ -0,0 +1,1309 @@
+using SharpDX;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YndFile : GameFile, PackedFile, BasePathData
+ {
+
+ public NodeDictionary NodeDictionary { get; set; }
+
+ public YndNode[] Nodes { get; set; }
+ public YndLink[] Links { get; set; }
+ public YndJunction[] Junctions { get; set; }
+
+ public EditorVertex[] LinkedVerts { get; set; }//populated by the space (needs to use grid of all ynd's!)
+ public EditorVertex[] TriangleVerts { get; set; } //used for junctions display
+ public Vector4[] NodePositions { get; set; }
+
+ public Vector3 BBMin { get; set; }
+ public Vector3 BBMax { get; set; }
+ public int CellX { get; set; }
+ public int CellY { get; set; }
+ public int AreaID
+ {
+ get
+ {
+ return CellY * 32 + CellX;
+ }
+ set
+ {
+ CellX = value % 32;
+ CellY = value / 32;
+ UpdateBoundingBox();
+ }
+ }
+
+ public PathBVH BVH { get; set; }
+
+
+
+ //fields used by the editor:
+ public bool HasChanged { get; set; } = false;
+ public List SaveWarnings = null;
+
+
+
+ public YndFile() : base(null, GameFileType.Ynd)
+ {
+ }
+ public YndFile(RpfFileEntry entry) : base(entry, GameFileType.Ynd)
+ {
+ }
+
+
+
+ public void Load(byte[] data)
+ {
+ //direct load from a raw, compressed ynd file (openIV-compatible format)
+
+ RpfFile.LoadResourceFile(this, data, 1);
+
+ Loaded = true;
+ LoadQueued = true;
+ }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+ RpfFileEntry = entry;
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ throw new Exception("File entry wasn't a resource! (is it binary data?)");
+ }
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+
+ NodeDictionary = rd.ReadBlock();
+
+ if (NodeDictionary != null)
+ {
+ if (NodeDictionary.Nodes != null)
+ {
+ var nodes = NodeDictionary.Nodes;
+ Nodes = new YndNode[nodes.Length];
+ for (int i = 0; i < nodes.Length; i++)
+ {
+ var n = new YndNode();
+ n.Init(this, nodes[i]);
+ Nodes[i] = n;
+ if (n.NodeID != i)
+ { } //never hit here - nodeid's have to match the index!
+ }
+ }
+ if ((NodeDictionary.JunctionRefs != null) && (NodeDictionary.Junctions != null))
+ {
+ var juncrefs = NodeDictionary.JunctionRefs;
+ var juncs = NodeDictionary.Junctions;
+ Junctions = new YndJunction[juncrefs.Length];
+ for (int i = 0; i < juncrefs.Length; i++)
+ {
+ var juncref = NodeDictionary.JunctionRefs[i];
+ if (juncref.JunctionID >= juncs.Length)
+ { continue; }
+
+ var j = new YndJunction();
+ j.Init(this, juncs[juncref.JunctionID], juncref);
+ j.Heightmap = new YndJunctionHeightmap(NodeDictionary.JunctionHeightmapBytes, j);
+ Junctions[i] = j;
+ }
+ }
+ }
+
+ UpdateAllNodePositions();
+
+ //links will be populated by the space... maybe move that code here?
+
+
+
+ string areaidstr = Name.ToLowerInvariant().Replace("nodes", "").Replace(".ynd", "");
+ int areaid = 0;
+ int.TryParse(areaidstr, out areaid);
+ AreaID = areaid;
+
+ UpdateBoundingBox();
+
+
+ BuildBVH();
+
+
+ Loaded = true;
+ LoadQueued = true;
+ }
+
+
+ public byte[] Save()
+ {
+
+ BuildStructs();
+
+ byte[] data = ResourceBuilder.Build(NodeDictionary, 1); //ynd is version 1...
+
+
+ return data;
+
+ }
+
+ public void BuildStructs()
+ {
+
+ List newlinks = new List();
+ List newjuncs = new List();
+ List newjuncrefs = new List();
+ List newjuncheightmaps = new List();
+
+ if (Nodes != null)
+ {
+ int count = Nodes.Length;
+ var nodes = new Node[count];
+ for (int i = 0; i < count; i++)
+ {
+ var node = Nodes[i];
+ if (node.Links != null)
+ {
+ node.LinkCount = node.Links.Length;
+ node.LinkID = (ushort)newlinks.Count;
+ for (int l = 0; l < node.Links.Length; l++)
+ {
+ var nlink = node.Links[l];
+ var rlink = nlink.RawData;
+ if (nlink.Node2 != null)
+ {
+ rlink.AreaID = nlink.Node2.AreaID;
+ rlink.NodeID = nlink.Node2.NodeID;
+ }
+ newlinks.Add(rlink);
+ }
+ }
+ else
+ {
+ node.LinkCount = 0;
+ }
+
+ //LinkCount = node.LinkCountFlags.Value >> 3;
+ //LinkCountUnk = node.LinkCountFlags.Value & 7;
+ byte lcflags = (byte)((node.LinkCount << 3) + (node.LinkCountUnk & 7));
+ node._RawData.LinkCountFlags = lcflags;
+
+ nodes[i] = node.RawData;
+
+ if ((node.HasJunction) && (node.Junction != null))
+ {
+ var nj = node.Junction;
+ var heightmapoff = newjuncheightmaps.Count;
+ var heightmap = nj.Heightmap.GetBytes();
+ nj._RawData.HeightmapPtr = (ushort)heightmapoff;
+ var jref = nj.RefData;
+ jref.AreaID = node.AreaID;
+ jref.NodeID = node.NodeID;
+ jref.JunctionID = (ushort)newjuncs.Count;
+ //jref.Unk0 = 0;//always 0?
+ nj.RefData = jref;//move this somewhere else..??
+ newjuncs.Add(nj.RawData);
+ newjuncrefs.Add(jref);
+ newjuncheightmaps.AddRange(heightmap);
+ }
+ }
+ NodeDictionary.Nodes = nodes;
+ NodeDictionary.NodesCount = (uint)count;
+ NodeDictionary.Links = newlinks.ToArray();
+ NodeDictionary.LinksCount = (uint)newlinks.Count;
+ NodeDictionary.Junctions = newjuncs.ToArray();
+ NodeDictionary.JunctionsCount = (uint)newjuncs.Count;
+ NodeDictionary.JunctionRefs = newjuncrefs.ToArray();
+ NodeDictionary.JunctionRefsCount0 = (ushort)newjuncrefs.Count;
+ NodeDictionary.JunctionRefsCount1 = (ushort)newjuncrefs.Count;
+ NodeDictionary.JunctionHeightmapBytes = newjuncheightmaps.ToArray();
+ NodeDictionary.JunctionHeightmapBytesCount = (uint)newjuncheightmaps.Count;
+ }
+ else
+ {
+ NodeDictionary.Nodes = null;
+ NodeDictionary.NodesCount = 0;
+ NodeDictionary.Links = null;
+ NodeDictionary.LinksCount = 0;
+ NodeDictionary.Junctions = null;
+ NodeDictionary.JunctionsCount = 0;
+ NodeDictionary.JunctionRefs = null;
+ NodeDictionary.JunctionRefsCount0 = 0;
+ NodeDictionary.JunctionRefsCount1 = 0;
+ NodeDictionary.JunctionHeightmapBytes = null;
+ NodeDictionary.JunctionHeightmapBytesCount = 0;
+ }
+ }
+
+
+
+ public YndNode AddNode()
+ {
+ int cnt = Nodes?.Length ?? 0;
+ YndNode yn = new YndNode();
+ Node n = new Node();
+ n.AreaID = (ushort)AreaID;
+ n.NodeID = (ushort)cnt;
+ yn.Init(this, n);
+
+ int ncnt = cnt + 1;
+ YndNode[] nnodes = new YndNode[ncnt];
+ for (int i = 0; i < cnt; i++)
+ {
+ nnodes[i] = Nodes[i];
+ }
+ nnodes[cnt] = yn;
+ Nodes = nnodes;
+ NodeDictionary.NodesCount = (uint)ncnt;
+
+ return yn;
+ }
+
+ public bool RemoveNode(YndNode node)
+ {
+ List newnodes = new List();
+ int cnt = Nodes?.Length ?? 0;
+ bool r = false;
+ int ri = -1;
+ for (int i = 0; i < cnt; i++)
+ {
+ var tn = Nodes[i];
+ if (tn != node)
+ {
+ newnodes.Add(tn);
+ }
+ else
+ {
+ r = true;
+ ri = i;
+ }
+ }
+ Nodes = newnodes.ToArray();
+ NodeDictionary.NodesCount = (uint)newnodes.Count;
+ NodeDictionary.NodesCountVehicle = Math.Min(NodeDictionary.NodesCountVehicle, NodeDictionary.NodesCount);
+ NodeDictionary.NodesCountPed = Math.Min(NodeDictionary.NodesCountPed, NodeDictionary.NodesCount);
+
+ //remap node ID's...
+ List remlinks = new List();
+ if (ri >= 0)
+ {
+ for (int i = 0; i < Nodes.Length; i++)
+ {
+ var n = Nodes[i];
+ if (n.NodeID != i)
+ {
+ n.NodeID = (ushort)i;
+
+
+ //update nodeid's in links...
+ for (int j = 0; j < Nodes.Length; j++)
+ {
+ var tn = Nodes[j];
+ if ((tn != null) && (tn.Links != null))
+ {
+ for (int bl = 0; bl < tn.Links.Length; bl++)
+ {
+ var backlink = tn.Links[bl];
+ if (backlink.Node2 == n)
+ {
+ backlink._RawData.NodeID = (ushort)i;
+ }
+ }
+ }
+ }
+ }
+
+ //remove any links referencing the node.
+ remlinks.Clear();
+ if (n.Links != null)
+ {
+ for (int l = 0; l < n.Links.Length; l++)
+ {
+ var nlink = n.Links[l];
+ if (nlink.Node2 == node)
+ {
+ remlinks.Add(nlink);
+ }
+ }
+ for (int l = 0; l < remlinks.Count; l++)
+ {
+ n.RemoveLink(remlinks[l]);
+ }
+ }
+
+ }
+
+ }
+
+ UpdateAllNodePositions();
+
+ return r;
+ }
+
+
+
+
+
+ public void UpdateBoundingBox()
+ {
+ Vector3 corner = new Vector3(-8192, -8192, -2048);
+ Vector3 cellsize = new Vector3(512, 512, 4096);
+
+ BBMin = corner + (cellsize * new Vector3(CellX, CellY, 0));
+ BBMax = BBMin + cellsize;
+ }
+
+ public void UpdateAllNodePositions()
+ {
+ int cnt = Nodes?.Length ?? 0;
+ if (cnt <= 0)
+ {
+ NodePositions = null;
+ return;
+ }
+ var np = new Vector4[cnt];
+ for (int i = 0; i < cnt; i++)
+ {
+ np[i] = new Vector4(Nodes[i].Position, 1.0f);
+ }
+ NodePositions = np;
+ }
+
+ public void UpdateTriangleVertices()
+ {
+ //note: called from space.BuildYndVerts()
+
+ UpdateLinkTriangleVertices();
+
+ //UpdateJunctionTriangleVertices();
+ }
+
+ private void UpdateLinkTriangleVertices()
+ {
+ //build triangles for the path links display
+
+
+ int vc = 0;
+ if (Links != null)
+ {
+ vc = Links.Length * 6;
+ }
+
+ List verts = new List(vc);
+ EditorVertex v0 = new EditorVertex();
+ EditorVertex v1 = new EditorVertex();
+ EditorVertex v2 = new EditorVertex();
+ EditorVertex v3 = new EditorVertex();
+ if ((Links != null) && (Nodes != null))
+ {
+ foreach (var node in Nodes)
+ {
+ foreach (var link in node.Links)
+ {
+ var p0 = link.Node1?.Position ?? Vector3.Zero;
+ var p1 = link.Node2?.Position ?? Vector3.Zero;
+ var diff = p1 - p0;
+ var dir = Vector3.Normalize(diff);
+ var ax = Vector3.Cross(dir, Vector3.UnitZ);
+
+
+ float lanestot = link.LaneCountForward + link.LaneCountBackward;
+ //float backfrac = Math.Min(Math.Max(link.LaneCountBackward / lanestot, 0.1f), 0.9f);
+ //float lanewidth = 7.0f;
+ //float inner = totwidth*(backfrac-0.5f);
+ //float outer = totwidth*0.5f;
+
+ float lanewidth = node.IsPedNode ? 0.5f : 5.5f;
+ float inner = link.LaneOffset * lanewidth;// 0.0f;
+ float outer = inner + Math.Max(lanewidth * link.LaneCountForward, 0.5f);
+
+ float totwidth = lanestot * lanewidth;
+ float halfwidth = totwidth * 0.5f;
+ if (link.LaneCountBackward == 0)
+ {
+ inner -= halfwidth;
+ outer -= halfwidth;
+ }
+ if (link.LaneCountForward == 0)
+ {
+ inner += halfwidth;
+ outer += halfwidth;
+ }
+
+
+ v0.Position = p1 + ax * inner;
+ v1.Position = p0 + ax * inner;
+ v2.Position = p1 + ax * outer;
+ v3.Position = p0 + ax * outer;
+ var c = (uint)link.GetColour().ToRgba();
+ v0.Colour = c;
+ v1.Colour = c;
+ v2.Colour = c;
+ v3.Colour = c;
+ verts.Add(v0);
+ verts.Add(v1);
+ verts.Add(v2);
+ verts.Add(v2);
+ verts.Add(v1);
+ verts.Add(v3);
+ }
+ }
+ }
+
+
+ if (verts.Count > 0)
+ {
+ TriangleVerts = verts.ToArray();
+ }
+ else
+ {
+ TriangleVerts = null;
+ }
+ }
+
+ private void UpdateJunctionTriangleVertices()
+ {
+ //build triangles for the junctions bytes display....
+
+ int vc = 0;
+ if (Junctions != null)
+ {
+ foreach (var j in Junctions)
+ {
+ var d = j.Heightmap;
+ if (d == null) continue;
+ vc += d.CountX * d.CountY * 6;
+ }
+ }
+
+ List verts = new List(vc);
+ EditorVertex v0 = new EditorVertex();
+ EditorVertex v1 = new EditorVertex();
+ EditorVertex v2 = new EditorVertex();
+ EditorVertex v3 = new EditorVertex();
+ if (Nodes != null)
+ {
+ foreach (var node in Nodes)
+ {
+ if (node.Junction == null) continue;
+ var j = node.Junction;
+ var d = j.Heightmap;
+ if (d == null) continue;
+
+ float maxz = j.MaxZ / 32.0f;
+ float minz = j.MinZ / 32.0f;
+ float rngz = maxz - minz;
+ float posx = j.PositionX / 4.0f;
+ float posy = j.PositionY / 4.0f;
+
+ Vector3 pos = new Vector3(posx, posy, 0.0f);
+ Vector3 siz = new Vector3(d.CountX, d.CountY, 0.0f) * 2.0f;
+ //Vector3 siz = new Vector3(jx, jy, 0.0f);
+ Vector3 cnr = pos;// - siz * 0.5f;
+ //Vector3 inc = new Vector3(1.0f/jx)
+
+ cnr.Z = minz;// + 2.0f;
+
+ for (int y = 1; y < d.CountY; y++) //rows progress up the Y axis.
+ {
+ var row0 = d.Rows[y - 1];
+ var row1 = d.Rows[y];
+ float offy = y * 2.0f;
+
+ for (int x = 1; x < d.CountX; x++) //values progress along the X axis.
+ {
+ var val0 = row0.Values[x - 1] / 255.0f;
+ var val1 = row0.Values[x] / 255.0f;
+ var val2 = row1.Values[x - 1] / 255.0f;
+ var val3 = row1.Values[x] / 255.0f;
+ float offx = x * 2.0f;
+ v0.Position = cnr + new Vector3(offx - 2.0f, offy - 2.0f, val0 * rngz);
+ v1.Position = cnr + new Vector3(offx + 0.0f, offy - 2.0f, val1 * rngz);
+ v2.Position = cnr + new Vector3(offx - 2.0f, offy + 0.0f, val2 * rngz);
+ v3.Position = cnr + new Vector3(offx + 0.0f, offy + 0.0f, val3 * rngz);
+ v0.Colour = (uint)new Color4(val0, 1.0f - val0, 0.0f, 1.0f).ToRgba();
+ v1.Colour = (uint)new Color4(val1, 1.0f - val1, 0.0f, 1.0f).ToRgba();
+ v2.Colour = (uint)new Color4(val2, 1.0f - val2, 0.0f, 1.0f).ToRgba();
+ v3.Colour = (uint)new Color4(val3, 1.0f - val3, 0.0f, 1.0f).ToRgba();
+ verts.Add(v0);
+ verts.Add(v1);
+ verts.Add(v2);
+ verts.Add(v2);
+ verts.Add(v1);
+ verts.Add(v3);
+ }
+ }
+
+
+ }
+ }
+
+ if (verts.Count > 0)
+ {
+ TriangleVerts = verts.ToArray();
+ }
+ else
+ {
+ TriangleVerts = null;
+ }
+
+
+ }
+
+
+ public void UpdateBvhForNode(YndNode node)
+ {
+ //this needs to be called when a node's position changes...
+ //if it changes a lot, need to recalc the BVH for mouse intersection optimisation purposes.
+
+ //if (BVH == null) return;
+ //BVH.UpdateForNode(node);
+
+ BuildBVH();
+
+ //also updates the NodePositions for the visible vertex
+ if (Nodes != null)
+ {
+ for (int i = 0; i < Nodes.Length; i++)
+ {
+ if (Nodes[i] == node)
+ {
+ NodePositions[i] = new Vector4(node.Position, 1.0f);
+ break;
+ }
+ }
+ }
+
+ }
+
+ public void BuildBVH()
+ {
+ BVH = new PathBVH(Nodes, 10, 10);
+ }
+
+
+
+
+ public EditorVertex[] GetPathVertices()
+ {
+ return LinkedVerts;
+ }
+ public EditorVertex[] GetTriangleVertices()
+ {
+ return TriangleVerts;
+ }
+ public Vector4[] GetNodePositions()
+ {
+ return NodePositions;
+ }
+
+
+
+ public override string ToString()
+ {
+ return RpfFileEntry?.ToString() ?? string.Empty;
+ }
+ }
+
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YndNode : BasePathNode
+ {
+ public Node _RawData;
+
+ public YndFile Ynd { get; set; }
+ public Node RawData { get { return _RawData; } set { _RawData = value; } }
+ public Vector3 Position { get; set; }
+ public int LinkCount { get; set; }
+ public int LinkCountUnk { get; set; }
+ public YndLink[] Links { get; set; }
+
+ public ushort AreaID { get { return _RawData.AreaID; } set { _RawData.AreaID = value; } }
+ public ushort NodeID { get { return _RawData.NodeID; } set { _RawData.NodeID = value; } }
+ public ushort LinkID { get { return _RawData.LinkID; } set { _RawData.LinkID = value; } }
+ public FlagsByte Flags0 { get { return _RawData.Flags0; } set { _RawData.Flags0 = value; } }
+ public FlagsByte Flags1 { get { return _RawData.Flags1; } set { _RawData.Flags1 = value; } }
+ public FlagsByte Flags2 { get { return _RawData.Flags2; } set { _RawData.Flags2 = value; } }
+ public FlagsByte Flags3 { get { return _RawData.Flags3; } set { _RawData.Flags3 = value; } }
+ public FlagsByte Flags4 { get { return _RawData.Flags4; } set { _RawData.Flags4 = value; } }
+ public TextHash StreetName { get { return _RawData.StreetName; } set { _RawData.StreetName = value; } }
+
+ public Color4 Colour { get; set; }
+
+ public YndJunction Junction { get; set; }
+ public bool HasJunction;
+
+
+ public bool IsPedNode
+ {
+ get
+ {
+ return false;// ((Flags4.Value >> 4) & 7) == 7;
+ }
+ }
+
+
+
+
+ public void Init(YndFile ynd, Node node)
+ {
+ Ynd = ynd;
+ RawData = node;
+ Vector3 p = new Vector3();
+ p.X = node.PositionX / 4.0f;
+ p.Y = node.PositionY / 4.0f;
+ p.Z = node.PositionZ / 32.0f;
+ Position = p;
+
+ LinkCount = node.LinkCountFlags.Value >> 3;
+ LinkCountUnk = node.LinkCountFlags.Value & 7;
+
+ Colour = GetColour();
+
+ }
+
+ public Color4 GetColour()
+ {
+ Color4 c = new Color4(LinkCountUnk / 7.0f, Flags0.Value / 255.0f, Flags1.Value / 255.0f, 1.0f);
+ //Color4 c = new Color4(0.0f, 0.0f, 0.0f, 1.0f);
+
+ //c.Red = (LinkCountUnk >> 1) / 3.0f;
+ //c.Red = (Flags3.Value >> 1) / 127.0f;
+ //c.Red = ((Flags4.Value >> 1) & 7) / 7.0f; //density value?
+ //c.Green = 1.0f - c.Red;
+
+
+ //if ((Flags0.Value & 1) > 0) c.Red += 1.0f; //script activated? N Yankton only + small piece in self storage
+ //if ((Flags0.Value & 2) > 0) c.Red += 1.0f; //car can use / gps?
+ //if ((Flags0.Value & 4) > 0) c.Red += 1.0f; //***not used
+ //if ((Flags0.Value & 8) > 0) c.Red += 1.0f; //gravel surface? country roads mostly
+ //if ((Flags0.Value & 16) > 0) c.Red += 1.0f; //***not used
+ //if ((Flags0.Value & 32) > 0) c.Red += 1.0f; //slow speed? hills roads, prison boundary, carparks, airport roads etc
+ //if ((Flags0.Value & 64) > 0) c.Red += 1.0f; //intersection entry 1 (has priority)?
+ //if ((Flags0.Value & 128) > 0) c.Green += 1.0f; //intersection entry 2 unk?
+
+ //if ((Flags1.Value & 1) > 0) c.Red += 1.0f; //left turn lane?
+ //if ((Flags1.Value & 2) > 0) c.Red += 1.0f; //left turn node of no return
+ //if ((Flags1.Value & 4) > 0) c.Red += 1.0f; //right turn node of no return
+ //if ((Flags1.Value & 8) > 0) c.Red += 1.0f; //entry for traffic lights / boom gates etc
+ //if ((Flags1.Value & 16) > 0) c.Red += 1.0f; //entry for traffic lights / boom gates etc + peds crossing
+ //if ((Flags1.Value & 32) > 0) c.Red += 1.0f; //intersection entry 3 unk?
+ //if ((Flags1.Value & 64) > 0) c.Red += 1.0f; //entry for traffic lights / boom gates etc + peds crossing
+ //if ((Flags1.Value & 128) > 0) c.Red += 1.0f; //intersection minor/stop, T?
+
+ ////[16 bits pos Z here]
+
+ //if ((Flags2.Value & 1) > 0) c.Red += 1.0f; //slow traffic? peds? carparks? military? GPS disable routing??
+ //if ((Flags2.Value & 2) > 0) c.Red += 1.0f; //***not used
+ //if ((Flags2.Value & 4) > 0) c.Red += 1.0f; //intersection decision?
+ //if ((Flags2.Value & 8) > 0) c.Red += 1.0f; //***not used
+ //if ((Flags2.Value & 16) > 0) c.Red += 1.0f; //slower traffic?
+ //if ((Flags2.Value & 32) > 0) c.Red += 1.0f; //water/boat
+ //if ((Flags2.Value & 64) > 0) c.Red += 1.0f; //freeways /peds?
+ //if ((Flags2.Value & 128) > 0) c.Red += 1.0f; //not a main road...?
+
+ //if ((LinkCountUnk & 1) > 0) c.Red += 1.0f; //has junction heightmap
+ //if ((LinkCountUnk & 2) > 0) c.Red += 1.0f; //speed/density/type related? not runways, not freeways
+ //if ((LinkCountUnk & 4) > 0) c.Red += 1.0f; //higher speed? eg freeway
+ ////[5 bits LinkCount here]
+
+ //if ((Flags3.Value & 1) > 0) c.Red += 1.0f; //is in an interior
+ //if ((Flags3.Value & 2) > 0) c.Red += 1.0f; //heuristic val?
+ //if ((Flags3.Value & 4) > 0) c.Red += 1.0f; //heuristic val?
+ //if ((Flags3.Value & 8) > 0) c.Red += 1.0f; //heuristic val?
+ //if ((Flags3.Value & 16) > 0) c.Red += 1.0f; //heuristic val?
+ //if ((Flags3.Value & 32) > 0) c.Red += 1.0f; //heuristic val?
+ //if ((Flags3.Value & 64) > 0) c.Red += 1.0f; //heuristic val?
+ //if ((Flags3.Value & 128) > 0) c.Red += 1.0f; //heuristic val?
+
+ //if ((Flags4.Value & 1) > 0) c.Red += 1.0f; //slow traffic?
+ //if ((Flags4.Value & 2) > 0) c.Red += 1.0f; //density/popgroup value..?
+ //if ((Flags4.Value & 4) > 0) c.Green += 1.0f; //density/popgroup value..?
+ //if ((Flags4.Value & 8) > 0) c.Blue += 1.0f; //density/popgroup value..?
+ //if ((Flags4.Value & 16) > 0) c.Red += 1.0f; //special/peds path?
+ //if ((Flags4.Value & 32) > 0) c.Green += 1.0f; //special/peds path?
+ //if ((Flags4.Value & 64) > 0) c.Blue += 1.0f; //special/peds path?
+ //if ((Flags4.Value & 128) > 0) c.Blue += 1.0f; //intersection entry left turn?
+
+
+
+
+
+
+ ////regarding paths.xml:
+ ////rubidium - Today at 8:37 AM
+ //also, quick glimpse over the xml for attributes:
+ ////> grep - i "attribute name" paths.xml | awk - F'^"' ' { print $2 }' | sort - u
+ //Block If No Lanes
+ //Cannot Go Left
+ //Cannot Go Right
+ //Density
+ //Disabled
+ //Dont Use For Navigation
+ //GpsBothWays
+ //Highway
+ //Indicate Keep Left
+ //Indicate Keep Right
+ //Lanes In
+ //Lanes Out
+ //Left Turns Only
+ //Narrowroad
+ //No Big Vehicles
+ //NoGps
+ //Off Road
+ //Shortcut
+ //Slip Lane
+ //Special
+ //Speed
+ //Streetname
+ //Tunnel
+ //Water
+ //Width
+
+
+
+
+ return c;
+ }
+
+
+ public void SetPosition(Vector3 pos)
+ {
+ _RawData.PositionX = (short)(pos.X * 4.0f);
+ _RawData.PositionY = (short)(pos.Y * 4.0f);
+ _RawData.PositionZ = (short)(pos.Z * 32.0f);
+
+ Vector3 newpos = pos;
+ //newpos.X = _RawData.PositionX / 4.0f;
+ //newpos.Y = _RawData.PositionY / 4.0f;
+ //newpos.Z = _RawData.PositionZ / 32.0f;
+ Position = newpos;
+
+ UpdateLinkLengths();
+ }
+
+
+ public void UpdateLinkLengths()
+ {
+ if (Links == null) return;
+ for (int i = 0; i < Links.Length; i++)
+ {
+ var link = Links[i];
+ link.UpdateLength(); //update this node's links
+
+ var n2 = link.Node2;
+ if ((n2 == null) || (n2.Links == null)) continue;
+
+ for (int j = 0; j < n2.Links.Length; j++)
+ {
+ var n2l = n2.Links[j];
+ if (n2l.Node2 == this)
+ {
+ n2l.UpdateLength(); //update back links
+ }
+ }
+ }
+ }
+
+
+ public YndLink AddLink(YndNode tonode = null)
+ {
+ YndLink l = new YndLink();
+ l._RawData.AreaID = AreaID;
+ l.Node1 = this;
+ if (tonode != null)
+ {
+ l.Node2 = tonode;
+ l._RawData.AreaID = tonode.AreaID;
+ l._RawData.NodeID = tonode.NodeID;
+ }
+ else if ((Ynd.Nodes != null) && (Ynd.Nodes.Length > 0))
+ {
+ l.Node2 = Ynd.Nodes[0];
+ }
+ else
+ {
+ l.Node2 = this;
+ l._RawData.NodeID = NodeID;
+ }
+ l.UpdateLength();
+
+ int cnt = Links?.Length ?? 0;
+ int ncnt = cnt + 1;
+ YndLink[] nlinks = new YndLink[ncnt];
+ for (int i = 0; i < cnt; i++)
+ {
+ nlinks[i] = Links[i];
+ }
+ nlinks[cnt] = l;
+ Links = nlinks;
+ LinkCount = ncnt;
+
+ return l;
+ }
+
+ public bool RemoveLink(YndLink l)
+ {
+ List newlinks = new List();
+ int cnt = Links?.Length ?? 0;
+ bool r = false;
+ for (int i = 0; i < cnt; i++)
+ {
+ var tl = Links[i];
+ if (tl != l)
+ {
+ newlinks.Add(tl);
+ }
+ else
+ {
+ r = true;
+ }
+ }
+ Links = newlinks.ToArray();
+ LinkCount = newlinks.Count;
+ return r;
+ }
+
+
+ public override string ToString()
+ {
+ //return AreaID.ToString() + "." + NodeID.ToString();
+ return StreetName.ToString() + ", " + Position.X.ToString() + ", " + Position.Y.ToString() + ", " + Position.Z.ToString() + ", " + NodeID.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YndLink
+ {
+ public YndFile Ynd { get; set; }
+ public YndNode Node1 { get; set; }
+ public YndNode Node2 { get; set; }
+ public NodeLink _RawData;
+ public NodeLink RawData { get { return _RawData; } set { _RawData = value; } }
+ public FlagsByte Flags0 { get { return _RawData.Flags0; } set { _RawData.Flags0 = value; } }
+ public FlagsByte Flags1 { get { return _RawData.Flags1; } set { _RawData.Flags1 = value; } }
+ public FlagsByte Flags2 { get { return _RawData.Flags2; } set { _RawData.Flags2 = value; } }
+ public FlagsByte LinkLength { get { return _RawData.LinkLength; } set { _RawData.LinkLength = value; } }
+
+ public int LaneCountForward { get { return (Flags2.Value >> 5) & 7; } }
+ public int LaneCountBackward { get { return (Flags2.Value >> 2) & 7; } }
+
+ public int OffsetValue { get { return (Flags1.Value >> 4) & 7; } }
+ public bool NegativeOffset { get { return (Flags1.Value >> 7) > 0; } }
+ public float LaneOffset { get { return (OffsetValue / 7.0f) * (NegativeOffset ? -0.5f : 0.5f); } }
+
+
+ public void Init(YndFile ynd, YndNode node1, YndNode node2, NodeLink link)
+ {
+ Ynd = ynd;
+ Node1 = node1;
+ Node2 = node2;
+ RawData = link;
+ }
+
+
+ public void UpdateLength()
+ {
+ if (Node1 == null) return;
+ if (Node2 == null) return;
+
+ LinkLength = (byte)Math.Min(255, (Node2.Position - Node1.Position).Length());
+ }
+
+
+ public void CopyFlags(YndLink link)
+ {
+ if (link == null) return;
+ Flags0 = link.Flags0;
+ Flags1 = link.Flags1;
+ Flags2 = link.Flags2;
+ }
+
+
+
+ public Color4 GetColour()
+ {
+
+
+ //float f0 = Flags0.Value / 255.0f;
+ //float f1 = Flags1.Value / 255.0f;
+ //float f2 = Flags2.Value / 255.0f;
+ //var c = new Color4(f0, f1, f2, 1.0f);
+
+ var c = new Color4(0.0f, 0.0f, 0.0f, 0.5f);
+ c.Green = LaneCountForward / 7.0f;
+ c.Red = LaneCountBackward / 7.0f;
+
+
+ //if ((Flags0.Value & 1) > 0) c.Red = 1.0f; //? some small pieces in city, roads at docks, and mall at beach
+ //if ((Flags0.Value & 2) > 0) c.Red = 1.0f; //3x segments joining east canal paths to roads, also josh's driveway - scripted?
+ //if ((Flags0.Value & 4) > 0) c.Red = 1.0f; //? looks fairly random, 0 for water, alternating - slope related?
+ //if ((Flags0.Value & 8) > 0) c.Red = 1.0f; //? like above
+ //if ((Flags0.Value & 16) > 0) c.Red = 1.0f; //? similar to above, but less
+ //if ((Flags0.Value & 32) > 0) c.Red = 1.0f; //? like above
+ //if ((Flags0.Value & 64) > 0) c.Red = 1.0f; //? slightly less random
+ //if ((Flags0.Value & 128) > 0) c.Red = 1.0f; //? still looks random...
+
+ //if ((Flags1.Value & 1) > 0) c.Red = 1.0f; //***not used?
+ //if ((Flags1.Value & 2) > 0) c.Red = 1.0f; //?possibly width/type bit
+ //if ((Flags1.Value & 4) > 0) c.Red = 1.0f; //avoid routing? no through road / no other exit?
+ //if ((Flags1.Value & 8) > 0) c.Red = 1.0f; //prefer routing? exit from dead end?
+ //if ((Flags1.Value & 16) > 0) c.Red = 1.0f; //offset value. mostly single lane, carpark access, golf course, alleyways, driveways, beach area etc
+ //if ((Flags1.Value & 32) > 0) c.Green = 1.0f; //offset value. similar to above
+ //if ((Flags1.Value & 64) > 0) c.Green = 1.0f; //offset value. similar to above
+ //if ((Flags1.Value & 128) > 0) c.Red = 1.0f; //offset value. (sign) similar to above (all paired with their back links!)
+
+ //if ((Flags2.Value & 1) > 0) c.Red = 1.0f; //angled link - merge? enter/exit divided road section, most big junctions, always paired
+ //if ((Flags2.Value & 2) > 0) c.Red = 1.0f; //lane change/u-turn link? always paired
+ //if ((Flags2.Value & 4) > 0) c.Red = 1.0f; //lane count back dir
+ //if ((Flags2.Value & 8) > 0) c.Red = 1.0f; //lane count back dir
+ //if ((Flags2.Value & 16) > 0) c.Red = 1.0f; //lane count back dir
+ //if ((Flags2.Value & 32) > 0) c.Green = 1.0f; //lane count forward dir
+ //if ((Flags2.Value & 64) > 0) c.Green = 1.0f; //lane count forward dir
+ //if ((Flags2.Value & 128) > 0) c.Green = 1.0f; //lane count forward dir
+
+ ////var lanesfwd = (Flags2.Value >> 5) & 7;
+ ////var lanesbck = (Flags2.Value >> 2) & 7;
+ //////if ((lanesfwd > 0) && (lanesbck > 0) && (lanesfwd != lanesbck))
+ //////{ }
+
+
+
+ //var t = (Flags1.Value >> 4)&1;
+ //c.Red = t / 1.0f;
+ //c.Green = 1.0f - c.Red;
+ ////if (((Flags1.Value & 128) > 0))// && ((Flags1.Value & 64) == 0))
+ ////{ c.Red += 1.0f; }
+
+
+
+ return c;
+ }
+
+
+
+ public override string ToString()
+ {
+ return Node2._RawData.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YndJunction
+ {
+ public YndFile Ynd { get; set; }
+ public NodeJunction _RawData;
+ public NodeJunction RawData { get { return _RawData; } set { _RawData = value; } }
+ public NodeJunctionRef RefData { get; set; }
+ public YndJunctionHeightmap Heightmap { get; set; }
+ public short MaxZ { get; set; }
+ public short MinZ { get; set; }
+ public short PositionX { get; set; }
+ public short PositionY { get; set; }
+
+ public void Init(YndFile ynd, NodeJunction junc, NodeJunctionRef reff)
+ {
+ Ynd = ynd;
+ RawData = junc;
+ RefData = reff;
+ MaxZ = junc.MaxZ;
+ MinZ = junc.MinZ;
+ PositionX = junc.PositionX;
+ PositionY = junc.PositionY;
+ }
+
+
+ public void ResizeHeightmap()
+ {
+ Heightmap.Resize(_RawData.HeightmapDimX, _RawData.HeightmapDimY);
+ }
+
+ public void SetHeightmap(string text)
+ {
+ Heightmap.SetData(text);
+ }
+
+
+ public override string ToString()
+ {
+ return RefData.ToString();
+ }
+
+
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YndJunctionHeightmap
+ {
+ public YndJunctionHeightmapRow[] Rows { get; set; }
+ public int CountX { get; set; }
+ public int CountY { get; set; }
+
+ public YndJunctionHeightmap(byte[] data, YndJunction junc)
+ {
+ if (data == null)
+ { return; }
+
+ var d = junc.RawData;
+ int s = d.HeightmapPtr;
+ CountX = d.HeightmapDimX;
+ CountY = d.HeightmapDimY;
+
+ if ((s + CountX * CountY) > data.Length)
+ { return; }
+
+ Rows = new YndJunctionHeightmapRow[CountY];
+
+
+ for (int y = 0; y < CountY; y++)
+ {
+ int o = s + y * CountX;
+ byte[] vals = new byte[CountX];
+ Buffer.BlockCopy(data, o, vals, 0, CountX);
+ Rows[y] = new YndJunctionHeightmapRow(vals);
+ }
+ }
+
+
+ public byte[] GetBytes()
+ {
+ int cnt = CountX * CountY;
+ var bytes = new byte[cnt];
+ for (int y = 0; y < CountY; y++)
+ {
+ int o = y * CountX;
+ var rowvals = Rows[y].Values;
+ Buffer.BlockCopy(rowvals, 0, bytes, o, CountX);
+ }
+ return bytes;
+ }
+
+
+ public void Resize(int cx, int cy)
+ {
+ var nrows = new YndJunctionHeightmapRow[cy];
+ for (int y = 0; y < cy; y++)
+ {
+ byte[] nvals = new byte[cx];
+ int minx = Math.Min(cx, CountX);
+ if ((Rows != null) && (y < Rows.Length))
+ {
+ Buffer.BlockCopy(Rows[y].Values, 0, nvals, 0, minx);
+ }
+ nrows[y] = new YndJunctionHeightmapRow(nvals);
+ }
+ Rows = nrows;
+
+ CountX = cx;
+ CountY = cy;
+ }
+
+ public void SetData(string text)
+ {
+ var rsplit = new[] { '\n' };
+ var csplit = new[] { ' ' };
+ string[] rowstrs = text.Split(rsplit, StringSplitOptions.RemoveEmptyEntries);
+ for (int y = 0; y < rowstrs.Length; y++)
+ {
+ string[] colstrs = rowstrs[y].Trim().Split(csplit, StringSplitOptions.RemoveEmptyEntries);
+ int cx = colstrs.Length;
+ byte[] vals = new byte[cx];
+ for (int x = 0; x < cx; x++)
+ {
+ byte.TryParse(colstrs[x], out vals[x]);
+ }
+ int minx = Math.Min(cx, CountX);
+ if ((Rows != null) && (y < Rows.Length))
+ {
+ var nrow = new byte[CountX];
+ Buffer.BlockCopy(vals, 0, nrow, 0, minx);
+ Rows[y].Values = nrow;
+ }
+ }
+ }
+
+ public string GetDataString()
+ {
+ StringBuilder sb = new StringBuilder();
+ if (Rows != null)
+ {
+ foreach (var row in Rows)
+ {
+ sb.AppendLine(row.ToString());
+ }
+ }
+ return sb.ToString();
+ }
+
+ public override string ToString()
+ {
+ return CountX.ToString() + " x " + CountY.ToString();
+ }
+ }
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YndJunctionHeightmapRow
+ {
+ public byte[] Values { get; set; }
+
+ public YndJunctionHeightmapRow(byte[] vals)
+ {
+ Values = vals;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < Values.Length; i++)
+ {
+ if (i > 0) sb.Append(" ");
+ sb.Append(Values[i].ToString().PadLeft(3, '0'));
+ //sb.Append(Convert.ToString(Values[i], 16).ToUpper().PadLeft(2, '0'));
+ }
+ return sb.ToString();
+ }
+ }
+
+
+
+
+ public class PathBVHNode
+ {
+ public int Depth;
+ public int MaxDepth;
+ public int Threshold;
+ public List Nodes;
+ public BoundingBox Box;
+ public BoundingSphere Sphere;
+ public PathBVHNode Node1;
+ public PathBVHNode Node2;
+
+
+ public void CalcBounds()
+ {
+ if ((Nodes == null) || (Nodes.Count <= 0)) return;
+
+ Box.Minimum = new Vector3(float.MaxValue);
+ Box.Maximum = new Vector3(float.MinValue);
+ foreach (var node in Nodes)
+ {
+ Box.Minimum = Vector3.Min(Box.Minimum, node.Position);
+ Box.Maximum = Vector3.Max(Box.Maximum, node.Position);
+ }
+ Sphere.Center = (Box.Minimum + Box.Maximum) * 0.5f;
+ Sphere.Radius = (Box.Maximum - Box.Minimum).Length() * 0.5f;
+ }
+
+
+ public void Build()
+ {
+ if ((Nodes == null) || (Nodes.Count <= Threshold) || (Depth >= MaxDepth)) return;
+
+ Vector3 avgsum = Vector3.Zero;
+ foreach (var node in Nodes)
+ {
+ avgsum += node.Position;
+ }
+ Vector3 avg = avgsum * (1.0f / Nodes.Count);
+
+ int countx = 0, county = 0, countz = 0;
+ foreach (var node in Nodes)
+ {
+ if (node.Position.X < avg.X) countx++;
+ if (node.Position.Y < avg.Y) county++;
+ if (node.Position.Z < avg.Z) countz++;
+ }
+
+ int target = Nodes.Count / 2;
+ int dx = Math.Abs(target - countx);
+ int dy = Math.Abs(target - county);
+ int dz = Math.Abs(target - countz);
+
+ int axis = -1;
+ if ((dx <= dy) && (dx <= dz)) axis = 0; //x seems best
+ else if (dy <= dz) axis = 1; //y seems best
+ else axis = 2; //z seems best
+
+
+ List l1 = new List();
+ List l2 = new List();
+ foreach (var node in Nodes)
+ {
+ bool s = false;
+ switch (axis)
+ {
+ default:
+ case 0: s = (node.Position.X > avg.X); break;
+ case 1: s = (node.Position.Y > avg.Y); break;
+ case 2: s = (node.Position.Z > avg.Z); break;
+ }
+ if (s) l1.Add(node);
+ else l2.Add(node);
+ }
+
+ var cdepth = Depth + 1;
+
+ Node1 = new PathBVHNode();
+ Node1.Depth = cdepth;
+ Node1.MaxDepth = MaxDepth;
+ Node1.Threshold = Threshold;
+ Node1.Nodes = new List(l1);
+ Node1.CalcBounds();
+ Node1.Build();
+
+ Node2 = new PathBVHNode();
+ Node2.Depth = cdepth;
+ Node2.MaxDepth = MaxDepth;
+ Node2.Threshold = Threshold;
+ Node2.Nodes = new List(l2);
+ Node2.CalcBounds();
+ Node2.Build();
+ }
+
+
+ public void UpdateForNode(BasePathNode node)
+ {
+ if (!Nodes.Contains(node)) return;
+ Box.Minimum = Vector3.Min(Box.Minimum, node.Position);
+ Box.Maximum = Vector3.Max(Box.Maximum, node.Position);
+
+ if (Node1 != null) Node1.UpdateForNode(node);
+ if (Node2 != null) Node2.UpdateForNode(node);
+ }
+
+ }
+
+ public class PathBVH : PathBVHNode
+ {
+
+ public PathBVH(IEnumerable nodes, int threshold, int maxdepth)
+ {
+ Threshold = threshold;
+ MaxDepth = maxdepth;
+ Nodes = (nodes != null) ? new List(nodes) : new List();
+ CalcBounds();
+ Build();
+ }
+
+ }
+
+
+
+
+ public interface BasePathNode
+ {
+ Vector3 Position { get; set; }
+ }
+
+ public interface BasePathData
+ {
+ //reuse this interface for file types that need to get paths rendered...
+
+ EditorVertex[] GetPathVertices();
+ EditorVertex[] GetTriangleVertices();
+ Vector4[] GetNodePositions();
+ }
+
+
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YnvFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YnvFile.cs
new file mode 100644
index 0000000..1fd6ea1
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YnvFile.cs
@@ -0,0 +1,689 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using SharpDX;
+
+namespace CodeWalker.GameFiles
+{
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvFile : GameFile, PackedFile, BasePathData
+ {
+ public NavMesh Nav { get; set; }
+
+ public List Vertices { get; set; }
+ public List Indices { get; set; }
+ public List AdjPolys { get; set; }
+ public List Polys { get; set; }
+ public List Portals { get; set; }
+ public List Points { get; set; }
+
+
+ public EditorVertex[] PathVertices { get; set; }
+ public EditorVertex[] TriangleVerts { get; set; }
+ public Vector4[] NodePositions { get; set; }
+
+
+ //fields used by the editor:
+ public bool HasChanged { get; set; } = false;
+ public List SaveWarnings = null;
+
+ public PathBVH BVH { get; set; }
+
+
+ public int AreaID
+ {
+ get
+ {
+ return (int)(Nav?.AreaID ?? 0);
+ }
+ set
+ {
+ if (Nav != null) Nav.AreaID = (uint)value;
+ }
+ }
+ public int CellX { get { return AreaID % 100; } set { AreaID = (CellY * 100) + value; } }
+ public int CellY { get { return AreaID / 100; } set { AreaID = (value * 100) + CellX; } }
+
+
+
+
+ public YnvFile() : base(null, GameFileType.Ynv)
+ {
+ }
+ public YnvFile(RpfFileEntry entry) : base(entry, GameFileType.Ynv)
+ {
+ }
+
+ public void Load(byte[] data)
+ {
+ //direct load from a raw, compressed ynv file (openIV-compatible format)
+
+ RpfFile.LoadResourceFile(this, data, 2);
+
+ Loaded = true;
+ }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+ RpfFileEntry = entry;
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ throw new Exception("File entry wasn't a resource! (is it binary data?)");
+ }
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+
+ Nav = rd.ReadBlock();
+
+
+ if (Nav != null)
+ {
+ Vector3 posoffset = Nav.SectorTree?.AABBMin.XYZ() ?? Vector3.Zero;
+ Vector3 aabbsize = Nav.AABBSize;
+
+ if (Nav.Vertices != null)
+ {
+ var verts = Nav.Vertices.GetFullList();
+ Vertices = new List(verts.Count);
+ for (int i = 0; i < verts.Count; i++)
+ {
+ var ov = verts[i].ToVector3();
+ Vertices.Add(posoffset + ov * aabbsize);
+ }
+ }
+ if (Nav.Indices != null)
+ {
+ Indices = Nav.Indices.GetFullList();
+ }
+ if (Nav.AdjPolys != null)
+ {
+ AdjPolys = Nav.AdjPolys.GetFullList();
+ }
+ if (Nav.Polys != null)
+ {
+ var polys = Nav.Polys.GetFullList();
+ Polys = new List(polys.Count);
+ for (int i = 0; i < polys.Count; i++)
+ {
+ YnvPoly poly = new YnvPoly();
+ poly.Init(this, polys[i]);
+ poly.Index = i;
+ poly.CalculatePosition(); //calc poly center for display purposes..
+ Polys.Add(poly);
+ }
+ }
+ if (Nav.Portals != null)
+ {
+ var portals = Nav.Portals;
+ Portals = new List(portals.Length);
+ for (int i = 0; i < portals.Length; i++)
+ {
+ YnvPortal portal = new YnvPortal();
+ portal.Init(this, portals[i]);
+ portal.Index = i;
+ portal.Position1 = posoffset + portal._RawData.Position1.ToVector3() * aabbsize;
+ portal.Position2 = posoffset + portal._RawData.Position2.ToVector3() * aabbsize;
+ Portals.Add(portal);
+ }
+ }
+
+
+ ////### add points to the list and calculate positions...
+ var treestack = new Stack();
+ var pointi = 0;
+ if (Nav.SectorTree != null)
+ {
+ treestack.Push(Nav.SectorTree);
+ }
+ while (treestack.Count > 0)
+ {
+ var sector = treestack.Pop();
+ if (sector.Data != null)
+ {
+ var points = sector.Data.Points;
+ if (points != null)
+ {
+ if (Points == null)
+ {
+ Points = new List();
+ }
+ for (int i = 0; i < points.Length; i++)
+ {
+ YnvPoint point = new YnvPoint();
+ point.Init(this, points[i]);
+ point.Index = pointi; pointi++;
+ point.Position = posoffset + point._RawData.Position * aabbsize;
+ Points.Add(point);
+ }
+ }
+ }
+ if (sector.SubTree1 != null) treestack.Push(sector.SubTree1);
+ if (sector.SubTree2 != null) treestack.Push(sector.SubTree2);
+ if (sector.SubTree3 != null) treestack.Push(sector.SubTree3);
+ if (sector.SubTree4 != null) treestack.Push(sector.SubTree4);
+ }
+
+ }
+
+
+
+ UpdateAllNodePositions();
+
+ UpdateTriangleVertices();
+
+ BuildBVH();
+
+
+ Loaded = true;
+ LoadQueued = true;
+ }
+
+
+ public byte[] Save()
+ {
+ BuildStructs();
+
+ byte[] data = ResourceBuilder.Build(Nav, 2); //ynv is version 2...
+
+ return data;
+ }
+
+ public void BuildStructs()
+ {
+ Vector3 posoffset = Nav.SectorTree?.AABBMin.XYZ() ?? Vector3.Zero;
+ Vector3 aabbsize = Nav.AABBSize;
+ Vector3 aabbsizeinv = 1.0f / aabbsize;
+
+ var vertlist = new List();
+ if (Vertices != null)
+ {
+ for (int i = 0; i < Vertices.Count; i++)
+ {
+ vertlist.Add(NavMeshVertex.Create((Vertices[i] - posoffset) * aabbsizeinv));
+ }
+ }
+ var polylist = new List();
+ if (Polys != null)
+ {
+ for (int i = 0; i < Polys.Count; i++)
+ {
+ polylist.Add(Polys[i].RawData);
+ }
+ }
+
+
+ if (Nav.Vertices == null)
+ {
+ Nav.Vertices = new NavMeshList();
+ Nav.Vertices.VFT = 1080158456;
+ }
+ if (Nav.Indices == null)
+ {
+ Nav.Indices = new NavMeshList();
+ Nav.Indices.VFT = 1080158424;
+ }
+ if (Nav.AdjPolys == null)
+ {
+ Nav.AdjPolys = new NavMeshList();
+ Nav.AdjPolys.VFT = 1080158440;
+ }
+ if (Nav.Polys == null)
+ {
+ Nav.Polys = new NavMeshList();
+ Nav.Polys.VFT = 1080158408;
+ }
+
+
+ Nav.Vertices.RebuildList(vertlist);
+
+ Nav.Indices.RebuildList(Indices);
+
+ Nav.AdjPolys.RebuildList(AdjPolys);
+
+ Nav.Polys.RebuildList(polylist);
+
+
+
+ for (int i = 0; i < Nav.Polys.ListParts.Count; i++) //reassign part id's on all the polys...
+ {
+ var listpart = Nav.Polys.ListParts[i];
+ var partitems = listpart?.Items;
+ if (partitems == null) continue;
+ ushort iu = (ushort)i;
+ for (int j = 0; j < partitems.Length; j++)
+ {
+ partitems[j].PartID = iu;
+ }
+ }
+
+ }
+
+
+
+
+
+ public bool RemovePoly(YnvPoly poly)
+ {
+ return false;
+ }
+
+
+
+
+
+ public void UpdateAllNodePositions()
+ {
+ if (Nav == null) return;
+
+
+ Vector3 posoffset = Nav.SectorTree.AABBMin.XYZ();
+ Vector3 aabbsize = Nav.AABBSize;
+
+ EditorVertex v = new EditorVertex();
+ v.Colour = 0xFF0000FF;
+ var lv = new List();
+ var nv = new List();
+
+
+ ////### add portal positions to the node list, also add links to the link vertex array
+ int cnt = Portals?.Count ?? 0;
+ if (cnt > 0)
+ {
+ for (int i = 0; i < cnt; i++)
+ {
+ var portal = Portals[i];
+ nv.Add(new Vector4(portal.Position1, 1.0f));
+ v.Position = portal.Position1; lv.Add(v);
+ v.Position = portal.Position2; lv.Add(v);
+ }
+ }
+
+
+ ////### add point positions to the node list
+ cnt = Points?.Count ?? 0;
+ if (cnt >= 0)
+ {
+ for (int i = 0; i < cnt; i++)
+ {
+ var point = Points[i];
+ nv.Add(new Vector4(point.Position, 1.0f));
+ }
+ }
+
+
+ NodePositions = (nv.Count > 0) ? nv.ToArray() : null;
+ PathVertices = (lv.Count > 0) ? lv.ToArray() : null;
+
+
+ }
+
+ public void UpdateTriangleVertices()
+ {
+ //need position and colour for each vertex.
+ //render as a triangle list... (no indices needed)
+
+ //go through the nav mesh polys and generate verts to render...
+
+ if ((Vertices == null) || (Vertices.Count == 0)) return;
+ if ((Indices == null) || (Indices.Count == 0)) return;
+ if ((Polys == null) || (Polys.Count == 0)) return;
+
+
+ int vc = Vertices.Count;
+
+ List rverts = new List();
+ foreach (var ypoly in Polys)
+ {
+ var poly = ypoly.RawData;
+ var colour = ypoly.GetColour();
+ var colourval = (uint)colour.ToRgba();
+
+ var ic = poly.IndexCount;
+ var startid = poly.IndexID;
+ var endid = startid + ic;
+ if (startid >= Indices.Count)
+ { continue; }
+ if (endid > Indices.Count)
+ { continue; }
+
+
+ if(ic<3)
+ { continue; }//not enough verts to make a triangle...
+
+ if (ic > 15)
+ { }
+
+
+ EditorVertex p0 = new EditorVertex();
+ EditorVertex p1 = new EditorVertex();
+ EditorVertex p2 = new EditorVertex();
+ p0.Colour = colourval;
+ p1.Colour = colourval;
+ p2.Colour = colourval;
+
+ var startind = Indices[startid];
+ if (startind >= vc)
+ { continue; }
+
+ p0.Position = Vertices[startind];
+
+ //build triangles for the poly.
+ int tricount = ic - 2;
+ for (int t = 0; t < tricount; t++)
+ {
+ int tid = startid + t;
+ int ind1 = Indices[tid + 1];
+ int ind2 = Indices[tid + 2];
+ if ((ind1 >= vc) || (ind2 >= vc))
+ { continue; }
+
+ p1.Position = Vertices[ind1];
+ p2.Position = Vertices[ind2];
+
+ rverts.Add(p0);
+ rverts.Add(p1);
+ rverts.Add(p2);
+ }
+
+ }
+
+ TriangleVerts = rverts.ToArray();
+
+ }
+
+
+
+ public void BuildBVH()
+ {
+ var nodes = new List();
+ if (Portals != null) nodes.AddRange(Portals);
+ if (Points != null) nodes.AddRange(Points);
+ BVH = new PathBVH(nodes, 10, 10);
+ }
+
+
+
+ public EditorVertex[] GetPathVertices()
+ {
+ return PathVertices;
+ }
+ public EditorVertex[] GetTriangleVertices()
+ {
+ return TriangleVerts;
+ }
+ public Vector4[] GetNodePositions()
+ {
+ return NodePositions;
+ }
+ }
+
+
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPoly
+ {
+ public NavMeshPoly _RawData;
+
+ public YnvFile Ynv { get; set; }
+ public NavMeshPoly RawData { get { return _RawData; } set { _RawData = value; } }
+
+ public ushort AreaID { get { return _RawData.AreaID; } set { _RawData.AreaID = value; } }
+ public ushort PartID { get { return _RawData.PartID; } set { _RawData.PartID = value; } }
+ public ushort PortalID { get { return _RawData.PortalID; } set { _RawData.PortalID = value; } }
+ public byte PortalUnk { get { return _RawData.PortalUnk; } set { _RawData.PortalUnk = value; } }
+ public byte Flags1 { get { return (byte)(_RawData.Unknown_00h & 0xFF); } set { _RawData.Unknown_00h = (ushort)((_RawData.Unknown_00h & 0xFF00) | (value & 0xFF)); } }
+ public byte Flags2 { get { return (byte)((_RawData.Unknown_24h.Value >> 0) & 0xFF); } set { _RawData.Unknown_24h = ((_RawData.Unknown_24h.Value & 0xFFFFFF00u) | ((value & 0xFFu) << 0)); } }
+ public byte Flags3 { get { return (byte)((_RawData.Unknown_24h.Value >> 9) & 0xFF); } set { _RawData.Unknown_24h = ((_RawData.Unknown_24h.Value & 0xFFFE01FFu) | ((value & 0xFFu) << 9)); } }
+ public byte Flags4 { get { return (byte)((_RawData.Unknown_28h.Value >> 16) & 0xFF); } set { _RawData.Unknown_28h = ((_RawData.Unknown_28h.Value & 0x0000FFFFu) | ((value & 0xFFu) << 16)); } }
+ public bool B00_AvoidUnk { get { return (_RawData.Unknown_00h & 1) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 0, value); } }
+ public bool B01_AvoidUnk { get { return (_RawData.Unknown_00h & 2) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 1, value); } }
+ public bool B02_IsFootpath { get { return (_RawData.Unknown_00h & 4) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 2, value); } }
+ public bool B03_IsUnderground { get { return (_RawData.Unknown_00h & 8) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 3, value); } }
+ //public bool B04_Unused { get { return (_RawData.Unknown_00h & 16) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 4, value); } }
+ //public bool B05_Unused { get { return (_RawData.Unknown_00h & 32) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 5, value); } }
+ public bool B06_SteepSlope { get { return (_RawData.Unknown_00h & 64) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 6, value); } }
+ public bool B07_IsWater { get { return (_RawData.Unknown_00h & 128) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 7, value); } }
+ public bool B08_UndergroundUnk0 { get { return (_RawData.Unknown_24h.Value & 1) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 0, value); } }
+ public bool B09_UndergroundUnk1 { get { return (_RawData.Unknown_24h.Value & 2) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 1, value); } }
+ public bool B10_UndergroundUnk2 { get { return (_RawData.Unknown_24h.Value & 4) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 2, value); } }
+ public bool B11_UndergroundUnk3 { get { return (_RawData.Unknown_24h.Value & 8) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 3, value); } }
+ //public bool B12_Unused { get { return (_RawData.Unknown_24h.Value & 16) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 4, value); } }
+ public bool B13_HasPathNode { get { return (_RawData.Unknown_24h.Value & 32) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 5, value); } }
+ public bool B14_IsInterior { get { return (_RawData.Unknown_24h.Value & 64) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 6, value); } }
+ public bool B15_InteractionUnk { get { return (_RawData.Unknown_24h.Value & 128) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 7, value); } }
+ //public bool B16_Unused { get { return (_RawData.Unknown_24h.Value & 256) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 8, value); } }
+ public bool B17_IsFlatGround { get { return (_RawData.Unknown_24h.Value & 512) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 9, value); } }
+ public bool B18_IsRoad { get { return (_RawData.Unknown_24h.Value & 1024) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 10, value); } }
+ public bool B19_IsCellEdge { get { return (_RawData.Unknown_24h.Value & 2048) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 11, value); } }
+ public bool B20_IsTrainTrack { get { return (_RawData.Unknown_24h.Value & 4096) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 12, value); } }
+ public bool B21_IsShallowWater { get { return (_RawData.Unknown_24h.Value & 8192) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 13, value); } }
+ public bool B22_FootpathUnk1 { get { return (_RawData.Unknown_24h.Value & 16384) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 14, value); } }
+ public bool B23_FootpathUnk2 { get { return (_RawData.Unknown_24h.Value & 32768) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 15, value); } }
+ public bool B24_FootpathMall { get { return (_RawData.Unknown_24h.Value & 65536) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 16, value); } }
+ public bool B25_SlopeSouth { get { return (_RawData.Unknown_28h.Value & 65536) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 16, value); } }
+ public bool B26_SlopeSouthEast { get { return (_RawData.Unknown_28h.Value & 131072) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 17, value); } }
+ public bool B27_SlopeEast { get { return (_RawData.Unknown_28h.Value & 262144) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 18, value); } }
+ public bool B28_SlopeNorthEast { get { return (_RawData.Unknown_28h.Value & 524288) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 19, value); } }
+ public bool B29_SlopeNorth { get { return (_RawData.Unknown_28h.Value & 1048576) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 20, value); } }
+ public bool B30_SlopeNorthWest { get { return (_RawData.Unknown_28h.Value & 2097152) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 21, value); } }
+ public bool B31_SlopeWest { get { return (_RawData.Unknown_28h.Value & 4194304) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 22, value); } }
+ public bool B32_SlopeSouthWest { get { return (_RawData.Unknown_28h.Value & 8388608) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 23, value); } }
+ //public bool B33_PortalUnk1 { get { return (_RawData.PortalUnk & 1) > 0; } }
+ //public bool B34_PortalUnk2 { get { return (_RawData.PortalUnk & 2) > 0; } }
+ //public bool B35_PortalUnk3 { get { return (_RawData.PortalUnk & 4) > 0; } }
+ //public bool B36_PortalUnk4 { get { return (_RawData.PortalUnk & 8) > 0; } }
+ public byte UnkX { get { return _RawData.Unknown_28h_8a; } set { _RawData.Unknown_28h_8a = value; } }
+ public byte UnkY { get { return _RawData.Unknown_28h_8b; } set { _RawData.Unknown_28h_8b = value; } }
+
+
+ public Vector3 Position { get; set; }
+ public int Index { get; set; }
+
+
+ public void Init(YnvFile ynv, NavMeshPoly poly)
+ {
+ Ynv = ynv;
+ RawData = poly;
+
+ }
+
+ public void SetPosition(Vector3 pos)
+ {
+ Vector3 delta = pos - Position;
+ Position = pos;
+ //TODO: update vertex positions!!!
+ }
+
+ public Color4 GetColour()
+ {
+ var colour = new Color4();
+ var u0 = _RawData.Unknown_00h;
+ if ((u0 & 1) > 0) colour.Red += 0.01f;//avoid? loiter?
+ if ((u0 & 2) > 0) colour.Red += 0.01f; //avoid?
+ if ((u0 & 4) > 0) colour.Green += 0.25f; //ped/footpath
+ if ((u0 & 8) > 0) colour.Green += 0.02f; //underground?
+ ////if ((u0 & 16) > 0) colour.Red += 1.0f; //not used?
+ ////if ((u0 & 32) > 0) colour.Green += 1.0f;//not used?
+ if ((u0 & 64) > 0) colour.Red += 0.25f; //steep slope
+ if ((u0 & 128) > 0) colour.Blue += 0.25f; //water
+ //if (u0 >= 256) colour.Green += 1.0f;//other bits unused...
+
+ var u2 = _RawData.Unknown_24h.Value;
+ //colour.Green = (u2 & 15) / 15.0f; //maybe underground amount..?
+ //if ((u2 & 1) > 0) colour.Blue += 1.0f; //peds interact with something? underground?
+ //if ((u2 & 2) > 0) colour.Green += 1.0f;//underneath something?
+ //if ((u2 & 4) > 0) colour.Red += 0.5f;//peds interact with something..? underground?
+ //if ((u2 & 8) > 0) colour.Red += 0.5f; //underground?
+ //if ((u2 & 16) > 0) colour.Red += 1.0f; //not used..
+ //if ((u2 & 32) > 0) colour.Green += 1.0f;//use path node?
+ if ((u2 & 64) > 0) colour.Blue += 0.1f; //is interior?
+ //if ((u2 & 128) > 0) colour.Red += 1.0f; //interacting areas? veg branches, roofs, vents, worker areas?
+ //if ((u2 & 256) > 0) colour.Green += 1.0f; //not used?
+ if ((u2 & 512) > 0) colour.Green += 0.1f;//is flat ground? ped-navigable?
+ if ((u2 & 1024) > 0) colour.Blue += 0.03f;//is a road
+ //if ((u2 & 2048) > 0) colour.Green += 1.0f; //poly is on a cell edge
+ if ((u2 & 4096) > 0) colour.Green += 0.75f; //is a train track
+ if ((u2 & 8192) > 0) colour.Blue += 0.75f;//shallow water/moving water
+ if ((u2 & 16384) > 0) colour.Red += 0.2f; //footpaths/beach - peds walking?
+ if ((u2 & 32768) > 0) colour.Blue += 0.2f; //footpaths - special?
+ if ((u2 & 65536) > 0) colour.Green = 0.2f;//footpaths - mall areas? eg mall, vinewood blvd
+ //if (u2 >= 131072) { }//other bits unused
+
+ var u5 = _RawData.Unknown_28h.Value; //32 bits
+ //colour.Red = poly.Unknown_28h_8a / 255.0f; //heuristic vals..?
+ //colour.Green = poly.Unknown_28h_8b / 255.0f; //heuristic vals..?
+ //if ((u5 & 65536) > 0) colour.Red += 1.0f; //slope facing -Y (south)
+ //if ((u5 & 131072) > 0) colour.Blue += 1.0f; //slope facing +X,-Y (southeast)
+ //if ((u5 & 262144) > 0) colour.Green += 1.0f; //slope facing +X (east)
+ //if ((u5 & 524288) > 0) colour.Red += 1.0f; //slope facing +X,+Y (northeast)
+ //if ((u5 & 1048576) > 0) colour.Green += 1.0f; //slope facing +Y (north)
+ //if ((u5 & 2097152) > 0) colour.Blue += 1.0f; //slope facing -X,+Y (northwest)
+ //if ((u5 & 4194304) > 0) colour.Green += 1.0f; //slope facing -X (west)
+ //if ((u5 & 8388608) > 0) colour.Red += 1.0f; //slope facing -X,-Y (southwest)
+ //if (u5 >= 16777216) { } //other bits unused
+
+ var u1 = _RawData.PortalUnk;
+ //if ((u1 & 1) > 0) colour.Red += 1.0f; //portal - don't interact?
+ //if ((u1 & 2) > 0) colour.Green += 1.0f; //portal - ladder/fence interaction?
+ //if ((u1 & 4) > 0) colour.Blue += 1.0f; //portal - fence interaction / go away from?
+ //if ((u1 & 8) > 0) colour.Red += 1.0f;//something file-specific? portal index related?
+
+
+
+ colour.Alpha = 0.75f;
+
+ return colour;
+ }
+
+
+
+ public void CalculatePosition()
+ {
+ //calc poly center for display purposes.
+ var indices = Ynv.Indices;
+ var vertices = Ynv.Vertices;
+ if ((indices == null) || (vertices == null))
+ { return; }
+ var vc = vertices.Count;
+ var ic = _RawData.IndexCount;
+ var startid = _RawData.IndexID;
+ var endid = startid + ic;
+ if (startid >= indices.Count)
+ { return; }
+ if (endid > indices.Count)
+ { return; }
+ Vector3 pcenter = Vector3.Zero;
+ float pcount = 0.0f;
+ for (int id = startid; id < endid; id++)
+ {
+ var ind = indices[id];
+ if (ind >= vc)
+ { continue; }
+
+ pcenter += vertices[ind];
+ pcount += 1.0f;
+ }
+ Position = pcenter * (1.0f / pcount);
+ }
+
+
+
+ public override string ToString()
+ {
+ return AreaID.ToString() + ", " + Index.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPortal : BasePathNode
+ {
+ public NavMeshPortal _RawData;
+
+ public YnvFile Ynv { get; set; }
+ public NavMeshPortal RawData { get { return _RawData; } set { _RawData = value; } }
+
+ public Vector3 Position { get { return Position1; } set { Position1 = value; } }
+ public Vector3 Position1 { get; set; }
+ public Vector3 Position2 { get; set; }
+ public int Index { get; set; }
+
+
+ public void Init(YnvFile ynv, NavMeshPortal portal)
+ {
+ Ynv = ynv;
+ RawData = portal;
+ }
+
+ public void SetPosition(Vector3 pos)
+ {
+ Position = pos;
+ //TODO: update _RawData positions!
+ }
+
+ public override string ToString()
+ {
+ return Index.ToString();
+ }
+ }
+
+ [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPoint : BasePathNode
+ {
+ public NavMeshPoint _RawData;
+
+ public YnvFile Ynv { get; set; }
+ public NavMeshPoint RawData { get { return _RawData; } set { _RawData = value; } }
+
+ public Vector3 Position { get; set; }
+ public float Direction
+ {
+ get
+ {
+ return (float)Math.PI * 2.0f * _RawData.Angle / 255.0f;
+ }
+ set
+ {
+ _RawData.Angle = (byte)(value * 255.0f / ((float)Math.PI * 2.0f));
+ }
+ }
+ public Quaternion Orientation
+ {
+ get { return Quaternion.RotationAxis(Vector3.UnitZ, Direction); }
+ set
+ {
+ Vector3 dir = value.Multiply(Vector3.UnitX);
+ float dira = (float)Math.Atan2(dir.Y, dir.X);
+ Direction = dira;
+ }
+ }
+
+ public int Index { get; set; }
+ public byte Flags { get { return _RawData.Flags; } set { _RawData.Flags = value; } }
+
+ public void Init(YnvFile ynv, NavMeshPoint point)
+ {
+ Ynv = ynv;
+ RawData = point;
+ }
+
+ public void SetPosition(Vector3 pos)
+ {
+ Position = pos;
+ //TODO! update _RawData.Position!!!
+ }
+ public void SetOrientation(Quaternion orientation)
+ {
+ Orientation = orientation;
+ }
+
+ public override string ToString()
+ {
+ return Index.ToString() + ": " + Flags.ToString();
+ }
+
+ }
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YptFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YptFile.cs
new file mode 100644
index 0000000..3a6530c
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YptFile.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ public class YptFile : GameFile, PackedFile
+ {
+ public ParticleEffectsList PtfxList { get; set; }
+
+ public Dictionary DrawableDict { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+
+ public YptFile() : base(null, GameFileType.Ypt)
+ {
+ }
+ public YptFile(RpfFileEntry entry) : base(entry, GameFileType.Ypt)
+ {
+ }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+ RpfFileEntry = entry;
+
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ throw new Exception("File entry wasn't a resource! (is it binary data?)");
+ }
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+ //MemoryUsage = 0;
+
+ try
+ {
+ PtfxList = rd.ReadBlock();
+ //Drawable.Owner = this;
+ //MemoryUsage += Drawable.MemoryUsage; //uses decompressed filesize now...
+ }
+ catch (Exception ex)
+ {
+ ErrorMessage = ex.ToString();
+ }
+
+
+ var dDict = PtfxList?.DrawableDictionary;
+
+ if ((dDict != null) &&
+ (dDict.Drawables != null) &&
+ (dDict.Drawables.data_items != null) &&
+ (dDict.Hashes != null))
+ {
+ DrawableDict = new Dictionary();
+ var drawables = dDict.Drawables.data_items;
+ var hashes = dDict.Hashes;
+ for (int i = 0; (i < drawables.Length) && (i < hashes.Length); i++)
+ {
+ var drawable = drawables[i];
+ var hash = hashes[i];
+ DrawableDict[hash] = drawable;
+ drawable.Owner = this;
+ }
+
+ for (int i = 0; (i < drawables.Length) && (i < hashes.Length); i++)
+ {
+ var drawable = drawables[i];
+ var hash = hashes[i];
+ if ((drawable.Name == null) || (drawable.Name.EndsWith("#dd")))
+ {
+ string hstr = JenkIndex.TryGetString(hash);
+ if (!string.IsNullOrEmpty(hstr))
+ {
+ drawable.Name = hstr;
+ }
+ else
+ {
+ drawable.Name = "0x" + hash.ToString("X").PadLeft(8, '0');
+ }
+ }
+ }
+
+ }
+
+
+
+
+
+ Loaded = true;
+
+ }
+
+
+ }
+
+
+
+
+
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YtdFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YtdFile.cs
new file mode 100644
index 0000000..5915e5f
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YtdFile.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ public class YtdFile : GameFile, PackedFile
+ {
+ public TextureDictionary TextureDict { get; set; }
+
+
+ public YtdFile() : base(null, GameFileType.Ytd)
+ {
+ }
+ public YtdFile(RpfFileEntry entry) : base(entry, GameFileType.Ytd)
+ {
+ }
+
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+
+
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ throw new Exception("File entry wasn't a resource! (is it binary data?)");
+ }
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+
+ TextureDict = rd.ReadBlock();
+
+ //MemoryUsage = 0; //uses decompressed file size now..
+ //if (TextureDict != null)
+ //{
+ // MemoryUsage += TextureDict.MemoryUsage;
+ //}
+
+ }
+
+
+ }
+}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YtypFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YtypFile.cs
new file mode 100644
index 0000000..f9d548e
--- /dev/null
+++ b/CodeWalker.Core/GameFiles/FileTypes/YtypFile.cs
@@ -0,0 +1,253 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.GameFiles
+{
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public class YtypFile : GameFile, PackedFile
+ {
+
+
+ public Meta Meta { get; set; }
+ public PsoFile Pso { get; set; }
+ public RbfFile Rbf { get; set; }
+
+
+ public uint NameHash { get; set; }
+ public string[] Strings { get; set; }
+
+
+ public CMapTypes CMapTypes { get; set; }
+
+ public Archetype[] AllArchetypes { get; set; }
+
+ public MetaWrapper[] Extensions { get; set; }
+
+ public CCompositeEntityType[] CompositeEntityTypes { get; set; }
+
+
+ //fields used by the editor:
+ public bool HasChanged { get; set; } = false;
+ public List SaveWarnings = null;
+
+
+
+ public YtypFile() : base(null, GameFileType.Ytyp)
+ {
+ }
+ public YtypFile(RpfFileEntry entry) : base(entry, GameFileType.Ytyp)
+ {
+ }
+
+
+ public override string ToString()
+ {
+ return (RpfFileEntry != null) ? RpfFileEntry.Name : string.Empty;
+ }
+
+
+ public void Load(byte[] data)
+ {
+ //direct load from a raw, compressed ytyp file (openIV-compatible format)
+
+ RpfFile.LoadResourceFile(this, data, 2);
+
+ Loaded = true;
+ }
+
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ Name = entry.Name;
+ RpfFileEntry = entry;
+ RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
+ if (resentry == null)
+ {
+ MemoryStream ms = new MemoryStream(data);
+ if (RbfFile.IsRBF(ms))
+ {
+ Rbf = new RbfFile();
+ Rbf.Load(ms);
+ }
+ else if (PsoFile.IsPSO(ms))
+ {
+ Pso = new PsoFile();
+ Pso.Load(ms);
+ //PsoTypes.EnsurePsoTypes(Pso);
+ }
+ else
+ {
+ }
+ return;
+ }
+
+
+
+
+
+ ResourceDataReader rd = new ResourceDataReader(resentry, data);
+
+ Meta = rd.ReadBlock();
+
+
+ CMapTypes = MetaTypes.GetTypedData(Meta, MetaName.CMapTypes);
+
+
+ List allarchs = new List();
+
+ var ptrs = MetaTypes.GetPointerArray(Meta, CMapTypes.archetypes);
+ if (ptrs != null)
+ {
+ for (int i = 0; i < ptrs.Length; i++)
+ {
+ var ptr = ptrs[i];
+ var offset = ptr.Offset;
+ var block = Meta.GetBlock(ptr.BlockID);
+ if (block == null)
+ { continue; }
+ if ((offset < 0) || (block.Data == null) || (offset >= block.Data.Length))
+ { continue; }
+
+ Archetype a = null;
+ switch (block.StructureNameHash)
+ {
+ case MetaName.CBaseArchetypeDef:
+ var basearch = PsoTypes.ConvertDataRaw(block.Data, offset);
+ a = new Archetype();
+ a.Init(this, ref basearch);
+ a.Extensions = MetaTypes.GetExtensions(Meta, basearch.extensions);
+ break;
+ case MetaName.CTimeArchetypeDef:
+ var timearch = PsoTypes.ConvertDataRaw(block.Data, offset);
+ var ta = new TimeArchetype();
+ ta.Init(this, ref timearch);
+ ta.Extensions = MetaTypes.GetExtensions(Meta, timearch._BaseArchetypeDef.extensions);
+ a = ta;
+ break;
+ case MetaName.CMloArchetypeDef:
+ var mloarch = PsoTypes.ConvertDataRaw(block.Data, offset);
+ var ma = new MloArchetype();
+ ma.Init(this, ref mloarch);
+ ma.Extensions = MetaTypes.GetExtensions(Meta, mloarch._BaseArchetypeDef.extensions);
+
+ ma.LoadChildren(Meta);
+
+ a = ma;
+ break;
+ default:
+ continue;
+ }
+
+ if (a != null)
+ {
+ allarchs.Add(a);
+ }
+ }
+ }
+ AllArchetypes = allarchs.ToArray();
+
+
+ Extensions = MetaTypes.GetExtensions(Meta, CMapTypes.extensions);
+ if (Extensions != null)
+ { }
+
+
+ //AudioEmitters = MetaTypes.GetTypedDataArray(Meta, MetaName.CExtensionDefAudioEmitter);
+ //if (AudioEmitters != null)
+ //{ }
+
+ //CEntityDefs = MetaTypes.GetTypedDataArray