diff --git a/BundleFormat/BundleArchive.cs b/BundleFormat/BundleArchive.cs
index 674138a..1d4166a 100644
--- a/BundleFormat/BundleArchive.cs
+++ b/BundleFormat/BundleArchive.cs
@@ -160,6 +160,45 @@ private void ProcessRST(string rst)
}
}
+ private void RebuildRST()
+ {
+ XmlDocument doc = new XmlDocument();
+ XmlElement root = doc.CreateElement("ResourceStringTable");
+ doc.AppendChild(root);
+
+ bool hasNamedResources = false;
+
+ foreach (BundleEntry entry in Entries)
+ {
+ string name = entry.DebugInfo.Name?.Trim();
+ if (string.IsNullOrEmpty(name))
+ continue;
+
+ XmlElement resource = doc.CreateElement("Resource");
+ resource.SetAttribute("id", entry.ID.ToString("X8"));
+ resource.SetAttribute("name", name);
+ resource.SetAttribute(
+ "type",
+ string.IsNullOrWhiteSpace(entry.DebugInfo.TypeName)
+ ? entry.Type.ToString()
+ : entry.DebugInfo.TypeName
+ );
+
+ root.AppendChild(resource);
+ hasNamedResources = true;
+ }
+
+ if (!hasNamedResources)
+ {
+ ResourceStringTable = null;
+ Flags &= ~Flags.HasResourceStringTable;
+ return;
+ }
+
+ ResourceStringTable = doc.OuterXml;
+ Flags |= Flags.HasResourceStringTable;
+ }
+
public static BundleArchive Read(string path)
{
using (Stream s = File.OpenRead(path))
@@ -580,6 +619,8 @@ public void Write(string path)
public void Write(BinaryWriter2 bw)
{
+ RebuildRST();
+
bw.Write(BND2Magic);
bw.Write(Version);
bw.Write((int)Platform);
diff --git a/BundleManager/ImportResourceForm.Designer.cs b/BundleManager/ImportResourceForm.Designer.cs
new file mode 100644
index 0000000..dc23dcb
--- /dev/null
+++ b/BundleManager/ImportResourceForm.Designer.cs
@@ -0,0 +1,270 @@
+namespace BundleManager
+{
+ partial class ImportResourceForm
+ {
+ ///
+ /// 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()
+ {
+ importButton = new System.Windows.Forms.Button();
+ resourceIDTextBox = new System.Windows.Forms.TextBox();
+ label1 = new System.Windows.Forms.Label();
+ resourceNameTextBox = new System.Windows.Forms.TextBox();
+ label2 = new System.Windows.Forms.Label();
+ label3 = new System.Windows.Forms.Label();
+ secondaryDataPathSelectorButton = new System.Windows.Forms.Button();
+ autoUpdateIDCheckBox = new System.Windows.Forms.CheckBox();
+ selectTypeLabel = new System.Windows.Forms.Label();
+ resourceTypeComboBox = new System.Windows.Forms.ComboBox();
+ tertiaryDataPathSelectorButton = new System.Windows.Forms.Button();
+ primaryImportPathLabel = new System.Windows.Forms.Label();
+ primaryResourcePathTextBox = new System.Windows.Forms.TextBox();
+ secondaryImportPathLabel = new System.Windows.Forms.Label();
+ secondaryResourcePathTextBox = new System.Windows.Forms.TextBox();
+ tertiaryImportPathLabel = new System.Windows.Forms.Label();
+ tertiaryResourcePathTextBox = new System.Windows.Forms.TextBox();
+ primaryDataPathSelectorButton = new System.Windows.Forms.Button();
+ SuspendLayout();
+ //
+ // importButton
+ //
+ importButton.Location = new System.Drawing.Point(562, 258);
+ importButton.Name = "importButton";
+ importButton.Size = new System.Drawing.Size(88, 23);
+ importButton.TabIndex = 0;
+ importButton.Text = "Import";
+ importButton.UseVisualStyleBackColor = true;
+ importButton.Click += importButton_Click;
+ //
+ // resourceIDTextBox
+ //
+ resourceIDTextBox.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper;
+ resourceIDTextBox.Location = new System.Drawing.Point(563, 27);
+ resourceIDTextBox.MaxLength = 8;
+ resourceIDTextBox.Name = "resourceIDTextBox";
+ resourceIDTextBox.Size = new System.Drawing.Size(85, 23);
+ resourceIDTextBox.TabIndex = 1;
+ resourceIDTextBox.Text = "XXXXXXXX";
+ //
+ // label1
+ //
+ label1.AutoSize = true;
+ label1.Location = new System.Drawing.Point(563, 9);
+ label1.Name = "label1";
+ label1.Size = new System.Drawing.Size(88, 15);
+ label1.TabIndex = 2;
+ label1.Text = "ID to import as:";
+ //
+ // resourceNameTextBox
+ //
+ resourceNameTextBox.Location = new System.Drawing.Point(12, 27);
+ resourceNameTextBox.Name = "resourceNameTextBox";
+ resourceNameTextBox.Size = new System.Drawing.Size(528, 23);
+ resourceNameTextBox.TabIndex = 3;
+ resourceNameTextBox.Text = "bundlemanager://my_really_cool_and_extremely_long_resource_name";
+ resourceNameTextBox.TextChanged += resourceNameTextBox_TextChanged;
+ //
+ // label2
+ //
+ label2.AutoSize = true;
+ label2.Location = new System.Drawing.Point(12, 9);
+ label2.Name = "label2";
+ label2.Size = new System.Drawing.Size(242, 15);
+ label2.TabIndex = 4;
+ label2.Text = "What would you like to name your resource?";
+ //
+ // label3
+ //
+ label3.AutoSize = true;
+ label3.Location = new System.Drawing.Point(546, 31);
+ label3.Name = "label3";
+ label3.Size = new System.Drawing.Size(15, 15);
+ label3.TabIndex = 5;
+ label3.Text = ">";
+ //
+ // secondaryDataPathSelectorButton
+ //
+ secondaryDataPathSelectorButton.Location = new System.Drawing.Point(508, 175);
+ secondaryDataPathSelectorButton.Name = "secondaryDataPathSelectorButton";
+ secondaryDataPathSelectorButton.Size = new System.Drawing.Size(140, 23);
+ secondaryDataPathSelectorButton.TabIndex = 6;
+ secondaryDataPathSelectorButton.Text = "Select Secondary Data";
+ secondaryDataPathSelectorButton.UseVisualStyleBackColor = true;
+ secondaryDataPathSelectorButton.Click += secondaryDataPathSelectorButton_Click;
+ //
+ // autoUpdateIDCheckBox
+ //
+ autoUpdateIDCheckBox.AutoSize = true;
+ autoUpdateIDCheckBox.Location = new System.Drawing.Point(546, 56);
+ autoUpdateIDCheckBox.Name = "autoUpdateIDCheckBox";
+ autoUpdateIDCheckBox.Size = new System.Drawing.Size(106, 19);
+ autoUpdateIDCheckBox.TabIndex = 7;
+ autoUpdateIDCheckBox.Text = "Auto update ID";
+ autoUpdateIDCheckBox.UseVisualStyleBackColor = true;
+ autoUpdateIDCheckBox.CheckedChanged += autoUpdateIDCheckBox_CheckedChanged;
+ //
+ // selectTypeLabel
+ //
+ selectTypeLabel.AutoSize = true;
+ selectTypeLabel.Location = new System.Drawing.Point(11, 60);
+ selectTypeLabel.Name = "selectTypeLabel";
+ selectTypeLabel.Size = new System.Drawing.Size(202, 15);
+ selectTypeLabel.TabIndex = 9;
+ selectTypeLabel.Text = "Select the resource type to import as:";
+ //
+ // resourceTypeComboBox
+ //
+ resourceTypeComboBox.FormattingEnabled = true;
+ resourceTypeComboBox.Location = new System.Drawing.Point(11, 78);
+ resourceTypeComboBox.Name = "resourceTypeComboBox";
+ resourceTypeComboBox.Size = new System.Drawing.Size(229, 23);
+ resourceTypeComboBox.TabIndex = 10;
+ resourceTypeComboBox.Text = "AptData";
+ //
+ // tertiaryDataPathSelectorButton
+ //
+ tertiaryDataPathSelectorButton.Location = new System.Drawing.Point(508, 220);
+ tertiaryDataPathSelectorButton.Name = "tertiaryDataPathSelectorButton";
+ tertiaryDataPathSelectorButton.Size = new System.Drawing.Size(140, 23);
+ tertiaryDataPathSelectorButton.TabIndex = 11;
+ tertiaryDataPathSelectorButton.Text = "Select Tertiary Data";
+ tertiaryDataPathSelectorButton.UseVisualStyleBackColor = true;
+ tertiaryDataPathSelectorButton.Click += tertiaryDataPathSelectorButton_Click;
+ //
+ // primaryImportPathLabel
+ //
+ primaryImportPathLabel.AutoSize = true;
+ primaryImportPathLabel.Location = new System.Drawing.Point(12, 114);
+ primaryImportPathLabel.Name = "primaryImportPathLabel";
+ primaryImportPathLabel.Size = new System.Drawing.Size(104, 15);
+ primaryImportPathLabel.TabIndex = 13;
+ primaryImportPathLabel.Text = "Primary data path:";
+ primaryImportPathLabel.TextAlign = System.Drawing.ContentAlignment.BottomLeft;
+ //
+ // primaryResourcePathTextBox
+ //
+ primaryResourcePathTextBox.Location = new System.Drawing.Point(12, 132);
+ primaryResourcePathTextBox.Name = "primaryResourcePathTextBox";
+ primaryResourcePathTextBox.Size = new System.Drawing.Size(490, 23);
+ primaryResourcePathTextBox.TabIndex = 14;
+ primaryResourcePathTextBox.Text = "my_really_cool_and_extremely_long_primary_resource_path";
+ //
+ // secondaryImportPathLabel
+ //
+ secondaryImportPathLabel.AutoSize = true;
+ secondaryImportPathLabel.Location = new System.Drawing.Point(12, 158);
+ secondaryImportPathLabel.Name = "secondaryImportPathLabel";
+ secondaryImportPathLabel.Size = new System.Drawing.Size(118, 15);
+ secondaryImportPathLabel.TabIndex = 15;
+ secondaryImportPathLabel.Text = "Secondary data path:";
+ secondaryImportPathLabel.TextAlign = System.Drawing.ContentAlignment.BottomLeft;
+ //
+ // secondaryResourcePathTextBox
+ //
+ secondaryResourcePathTextBox.Location = new System.Drawing.Point(12, 176);
+ secondaryResourcePathTextBox.Name = "secondaryResourcePathTextBox";
+ secondaryResourcePathTextBox.Size = new System.Drawing.Size(490, 23);
+ secondaryResourcePathTextBox.TabIndex = 16;
+ secondaryResourcePathTextBox.Text = "my_really_cool_and_extremely_long_secondary_resource_path";
+ //
+ // tertiaryImportPathLabel
+ //
+ tertiaryImportPathLabel.AutoSize = true;
+ tertiaryImportPathLabel.Location = new System.Drawing.Point(12, 202);
+ tertiaryImportPathLabel.Name = "tertiaryImportPathLabel";
+ tertiaryImportPathLabel.Size = new System.Drawing.Size(101, 15);
+ tertiaryImportPathLabel.TabIndex = 17;
+ tertiaryImportPathLabel.Text = "Tertiary data path:";
+ tertiaryImportPathLabel.TextAlign = System.Drawing.ContentAlignment.BottomLeft;
+ //
+ // tertiaryResourcePathTextBox
+ //
+ tertiaryResourcePathTextBox.Location = new System.Drawing.Point(12, 220);
+ tertiaryResourcePathTextBox.Name = "tertiaryResourcePathTextBox";
+ tertiaryResourcePathTextBox.Size = new System.Drawing.Size(490, 23);
+ tertiaryResourcePathTextBox.TabIndex = 18;
+ tertiaryResourcePathTextBox.Text = "my_really_cool_and_extremely_long_tertiary_resource_path";
+ //
+ // primaryDataPathSelectorButton
+ //
+ primaryDataPathSelectorButton.Location = new System.Drawing.Point(508, 132);
+ primaryDataPathSelectorButton.Name = "primaryDataPathSelectorButton";
+ primaryDataPathSelectorButton.Size = new System.Drawing.Size(140, 23);
+ primaryDataPathSelectorButton.TabIndex = 19;
+ primaryDataPathSelectorButton.Text = "Select Primary Data";
+ primaryDataPathSelectorButton.UseVisualStyleBackColor = true;
+ primaryDataPathSelectorButton.Click += primaryDataPathSelectorButton_Click;
+ //
+ // ImportResourceForm
+ //
+ AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ ClientSize = new System.Drawing.Size(659, 293);
+ Controls.Add(primaryDataPathSelectorButton);
+ Controls.Add(tertiaryResourcePathTextBox);
+ Controls.Add(tertiaryImportPathLabel);
+ Controls.Add(secondaryResourcePathTextBox);
+ Controls.Add(secondaryImportPathLabel);
+ Controls.Add(primaryResourcePathTextBox);
+ Controls.Add(primaryImportPathLabel);
+ Controls.Add(tertiaryDataPathSelectorButton);
+ Controls.Add(resourceTypeComboBox);
+ Controls.Add(selectTypeLabel);
+ Controls.Add(autoUpdateIDCheckBox);
+ Controls.Add(secondaryDataPathSelectorButton);
+ Controls.Add(label3);
+ Controls.Add(label2);
+ Controls.Add(resourceNameTextBox);
+ Controls.Add(label1);
+ Controls.Add(resourceIDTextBox);
+ Controls.Add(importButton);
+ Name = "ImportResourceForm";
+ Text = "Import Resource into Bundle";
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button importButton;
+ private System.Windows.Forms.TextBox resourceIDTextBox;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.TextBox resourceNameTextBox;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.Button secondaryDataPathSelectorButton;
+ private System.Windows.Forms.CheckBox autoUpdateIDCheckBox;
+ private System.Windows.Forms.Label selectTypeLabel;
+ private System.Windows.Forms.ComboBox resourceTypeComboBox;
+ private System.Windows.Forms.Button tertiaryDataPathSelectorButton;
+ private System.Windows.Forms.Label primaryImportPathLabel;
+ private System.Windows.Forms.TextBox primaryResourcePathTextBox;
+ private System.Windows.Forms.Label secondaryImportPathLabel;
+ private System.Windows.Forms.TextBox secondaryResourcePathTextBox;
+ private System.Windows.Forms.Label tertiaryImportPathLabel;
+ private System.Windows.Forms.TextBox tertiaryResourcePathTextBox;
+ private System.Windows.Forms.Button primaryDataPathSelectorButton;
+ }
+}
diff --git a/BundleManager/ImportResourceForm.cs b/BundleManager/ImportResourceForm.cs
new file mode 100644
index 0000000..0ac6085
--- /dev/null
+++ b/BundleManager/ImportResourceForm.cs
@@ -0,0 +1,659 @@
+using BundleFormat;
+using BundleUtilities;
+using BurnoutImage;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Windows.Forms;
+
+namespace BundleManager
+{
+ public partial class ImportResourceForm : Form
+ {
+ private const string ResourceNamePrefix = "bundlemanager://";
+
+ MemoryStream ms;
+ DataChunkImportProperties Properties = new();
+ public bool AutoUpdateID;
+ public BundleArchive ResultArchive { get; private set; }
+
+ private BundleArchive _workingArchive;
+
+ private BundleEntry _newEntry;
+
+
+ string ImportName
+ {
+ get => _importName;
+ set
+ {
+ _importName = value ?? string.Empty;
+ SetResourceNameText(ResourceNamePrefix + _importName, ResourceNamePrefix.Length + _importName.Length);
+ }
+ }
+ private string _importName;
+ private bool _isUpdatingResourceNameText;
+
+ public ImportResourceForm(BundleArchive destination, string primaryDataPath, uint? resourceID = null)
+ {
+ InitializeComponent();
+
+ _workingArchive = destination;
+
+ if (!Path.Exists(primaryDataPath))
+ throw new InvalidDataException("Selected file does not exist or is corrupt!");
+
+ ms = new(File.ReadAllBytes(primaryDataPath));
+
+ ImportName = Path.GetFileNameWithoutExtension(primaryDataPath);
+
+ RegisterDataBlockProperties();
+ Properties.Entries[0].Path = primaryDataPath;
+
+ ResolveDependencies();
+
+ Properties.EntryType = InferEntryType(ref ms);
+ _newEntry.Type = Properties.EntryType;
+
+ if (Properties.EntryType == EntryType.Invalid)
+ selectTypeLabel.Text = "Unable to auto detect resource type, please select it from the box below:";
+
+ autoUpdateIDCheckBox.Checked = resourceID == null ? true : false;
+
+ resourceTypeComboBox.DataSource = Enum.GetValues(typeof(EntryType));
+ resourceTypeComboBox.SelectedIndexChanged += ResourceTypeComboBox_SelectedIndexChanged;
+ resourceTypeComboBox.SelectedItem = Properties.EntryType;
+
+ resourceIDTextBox.KeyPress += ResourceIDTextBox_KeyPress;
+ resourceIDTextBox.Validating += ResourceIDTextBox_Validating;
+
+ primaryResourcePathTextBox.Validated += UpdateDataPathsFromTextBoxes;
+ secondaryResourcePathTextBox.Validated += UpdateDataPathsFromTextBoxes;
+ tertiaryResourcePathTextBox.Validated += UpdateDataPathsFromTextBoxes;
+ }
+
+ #region Event Handlers
+
+ private void ResourceIDTextBox_Validating(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ if (!Utilities.IsValidHex(resourceIDTextBox.Text))
+ {
+ e.Cancel = true;
+ MessageBox.Show("Please enter a valid hexadecimal value.");
+ }
+ }
+
+ private void ResourceIDTextBox_KeyPress(object sender, KeyPressEventArgs e)
+ {
+ bool isControl = char.IsControl(e.KeyChar);
+ bool isHex = Uri.IsHexDigit(e.KeyChar);
+
+ if (!isControl && !isHex)
+ e.Handled = true;
+ }
+
+ private void ResourceTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (resourceTypeComboBox.SelectedItem is EntryType entryType)
+ {
+ Properties.EntryType = entryType;
+ _newEntry.Type = entryType;
+ }
+ }
+
+ private void autoUpdateIDCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ AutoUpdateID = autoUpdateIDCheckBox.Checked;
+ resourceIDTextBox.ReadOnly = autoUpdateIDCheckBox.Checked;
+
+ if (AutoUpdateID)
+ RecalculateResourceIDTextBox();
+ }
+
+ private void resourceNameTextBox_TextChanged(object sender, EventArgs e)
+ {
+ if (_isUpdatingResourceNameText)
+ return;
+
+ string text = resourceNameTextBox.Text ?? string.Empty;
+
+ _importName = text.StartsWith(ResourceNamePrefix, StringComparison.OrdinalIgnoreCase)
+ ? text.Substring(ResourceNamePrefix.Length)
+ : text;
+
+ if (AutoUpdateID)
+ RecalculateResourceIDTextBox();
+ }
+
+ private void primaryDataPathSelectorButton_Click(object sender, EventArgs e)
+ {
+ SelectNewDataChunkDialog(0);
+ }
+
+ private void secondaryDataPathSelectorButton_Click(object sender, EventArgs e)
+ {
+ SelectNewDataChunkDialog(1);
+ }
+
+ private void tertiaryDataPathSelectorButton_Click(object sender, EventArgs e)
+ {
+ SelectNewDataChunkDialog(2);
+ }
+
+ private void importButton_Click(object sender, EventArgs e)
+ {
+ RunImportOperation();
+ }
+#endregion
+
+ void RegisterDataBlockProperties()
+ {
+ Properties.Entries[0].PathSelectorControls =
+ [
+ primaryDataPathSelectorButton,
+ primaryResourcePathTextBox,
+ primaryImportPathLabel
+ ];
+ Properties.Entries[1].PathSelectorControls =
+ [
+ secondaryDataPathSelectorButton,
+ secondaryResourcePathTextBox,
+ secondaryImportPathLabel
+ ];
+ Properties.Entries[2].PathSelectorControls =
+ [
+ tertiaryDataPathSelectorButton,
+ tertiaryResourcePathTextBox,
+ tertiaryImportPathLabel
+ ];
+
+ for (int i = 0; i < Properties.Entries.Length; i++)
+ {
+ Properties.Entries[i].PathChanged = OnAnyDataBlockPathsUpdated;
+ }
+
+ OnAnyDataBlockPathsUpdated();
+ }
+
+ void ResolveDependencies()
+ {
+ byte[] primaryData = ms.ToArray();
+
+ LoadDependencies(
+ primaryData,
+ _workingArchive.Platform > BundlePlatform.PC,
+ Properties.Entries[0].Path,
+ out List dependencies,
+ out int dependencyBytes
+ );
+
+ int newPrimarySize = primaryData.Length - dependencyBytes;
+ if (newPrimarySize < 0) newPrimarySize = 0;
+
+ byte[] strippedPrimary = new byte[newPrimarySize];
+ if (newPrimarySize > 0)
+ Buffer.BlockCopy(primaryData, 0, strippedPrimary, 0, newPrimarySize);
+
+ _newEntry = new(_workingArchive)
+ {
+ Type = Properties.EntryType
+ };
+
+ foreach (Dependency dep in dependencies)
+ _newEntry.Dependencies.Add(dep);
+
+ _newEntry.DependencyCount = (short)_newEntry.Dependencies.Count;
+
+ ms.SetLength(strippedPrimary.Length);
+
+ MessageBox.Show($"Resolved {_newEntry.Dependencies.Count} dependencies for the imported resource.");
+ }
+
+ // This really needs to be put in a better spot
+ EntryType InferEntryType(ref MemoryStream memoryStream)
+ {
+ long originalPos = memoryStream.Position;
+ byte[] data = memoryStream.ToArray();
+
+ // AttribSysVault
+ if (_workingArchive.Console switch
+ {
+ true => Utilities.ContainsASCII(data, "StrN")
+ && Utilities.ContainsASCII(data, "Vers"),
+ false => Utilities.ContainsASCII(data, "NrtS")
+ && Utilities.ContainsASCII(data, "sreV")
+ } == true)
+ {
+ return EntryType.AttribSysVault;
+ }
+
+ EntryType type = EntryType.Invalid;
+
+ memoryStream.Seek(0x0, SeekOrigin.Begin);
+
+ // Texture
+ if (GameImage.DetectPlatform(data) is PlatformType p
+ && p != PlatformType.Invalid
+ && _newEntry.Dependencies.Count == 0)
+ {
+ type = EntryType.Texture;
+ }
+ // Model
+ else if (memoryStream.Length <= 0x20)
+ {
+ // 32 bit model
+ memoryStream.Seek(0x13, SeekOrigin.Begin);
+ byte modelCandidate32 = (byte)memoryStream.ReadByte();
+
+ // 64 bit model (32 bit is technically padded to nearest 0x10, so this would run anyway)
+ if (memoryStream.Length > 0x14)
+ {
+ memoryStream.Seek(0x1F, SeekOrigin.Begin);
+
+ if (memoryStream.ReadByte() == 0x2)
+ type = EntryType.Model;
+ }
+
+ if (modelCandidate32 == 0x2)
+ type = EntryType.Model;
+ }
+ // Renderable
+ else
+ {
+ if (_workingArchive.Console switch
+ {
+ true => Utilities.ReadUInt16BE(data, 0x10),
+ false => Utilities.ReadUInt16LE(data, 0x10)
+ } == 11) // mu16VersionNumber
+ {
+ type = EntryType.Renderable;
+ }
+ }
+
+ memoryStream.Position = originalPos;
+ return type;
+ }
+
+ void OnAnyDataBlockPathsUpdated()
+ {
+ primaryResourcePathTextBox.Text = Properties.Entries[0].Path;
+ secondaryResourcePathTextBox.Text = Properties.Entries[1].Path;
+ tertiaryResourcePathTextBox.Text = Properties.Entries[2].Path;
+ }
+
+ private void UpdateDataPathsFromTextBoxes(object sender, EventArgs e)
+ {
+ if (sender is not TextBox textBox)
+ return;
+
+ string path = textBox.Text;
+
+ for (int i = 0; i < Properties.Entries.Length; i++)
+ {
+ if (Properties.Entries[i].Path != path)
+ Properties.Entries[i].Path = path;
+ }
+ }
+
+ private void RecalculateResourceIDTextBox()
+ {
+ string nameText = resourceNameTextBox.Text ?? string.Empty;
+
+ Utilities.CalculateResourceIDFromName
+ (
+ string.Concat
+ (
+ nameText.ToLowerInvariant(),
+ ".",
+ Properties.EntryType.ToString()
+ ),
+ out string id
+ );
+ resourceIDTextBox.Text = id;
+ }
+
+ void SelectNewDataChunkDialog(byte index)
+ {
+ using (OpenFileDialog ofd = new())
+ {
+ ofd.Title = $"Please select the {GetDataChunkName(index)} resource to import";
+ ofd.Filter = "Binary Files|*.dat;*.bin|Image Files(*.dds,*.bmp;*.gif;*.jpg;*.png;*.tif;*.tga;*.webp)|*.dds;*.bmp;*.gif;*.jpg;*.png;*.tif;*.tga;*.webp|All files (*.*)|*.*";
+
+ if (ofd.ShowDialog(this) != DialogResult.OK)
+ return;
+
+ string path = ofd.FileName;
+
+ if (string.IsNullOrEmpty(path) || !File.Exists(path))
+ return;
+
+ Properties.Entries[index].Path = path;
+ }
+ }
+
+ private void RunImportOperation()
+ {
+ for (int i = 1; i < Properties.DataChunkCount; i++)
+ {
+ if (!File.Exists(Properties.Entries[i].Path))
+ {
+ MessageBox.Show($"Please select valid {GetDataChunkName(i)} data for the resource you are trying to import.");
+ return;
+ }
+ }
+
+ byte[][] dataBlocks = new byte[Properties.DataChunkCount][];
+
+ dataBlocks[0] = ms.ToArray();
+ if (Properties.DataChunkCount > 1)
+ {
+ for (int i = 1; i < Properties.DataChunkCount; i++)
+ {
+ dataBlocks[i] = File.ReadAllBytes(Properties.Entries[i].Path);
+ }
+ }
+
+ EntryBlock block1 = new()
+ {
+ Compressed = false,
+ Data = dataBlocks[0]
+ };
+
+ EntryBlock block2 = new()
+ {
+ Compressed = false
+ };
+
+ EntryBlock block3 = new()
+ {
+ Compressed = false
+ };
+
+ if (Properties.DataChunkCount > 1)
+ {
+ if (Properties.EntryType == EntryType.Texture)
+ block2.Compressed = true;
+
+ block2.Data = dataBlocks[1];
+ }
+
+ _newEntry.EntryBlocks =
+ [
+ block1,
+ block2,
+ block3
+ ];
+
+ _newEntry.Dirty = true;
+ _newEntry.Type = Properties.EntryType;
+
+ _newEntry.ID = Convert.ToUInt32(resourceIDTextBox.Text, 16);
+ _newEntry.Index = _workingArchive.Entries.Count;
+ _newEntry.DebugInfo = new()
+ {
+ Name = $"{resourceNameTextBox.Text}.{Properties.EntryType.ToString()}",
+ TypeName = Properties.EntryType.ToString()
+ };
+
+ _workingArchive.Entries.Add(_newEntry);
+
+ ResultArchive = _workingArchive;
+ DialogResult = DialogResult.OK;
+ Close();
+ }
+
+ #region Dependency Handling
+
+ private void SetResourceNameText(string text, int caretPosition)
+ {
+ _isUpdatingResourceNameText = true;
+ resourceNameTextBox.Text = text;
+ resourceNameTextBox.SelectionStart = Math.Clamp(caretPosition, 0, resourceNameTextBox.TextLength);
+ resourceNameTextBox.SelectionLength = 0;
+ _isUpdatingResourceNameText = false;
+ }
+
+ private static void LoadDependencies(
+ byte[] primaryData,
+ bool isBigEndian,
+ string primaryPath,
+ out List dependencies,
+ out int dependencyBytes)
+ {
+ string yamlPath = GetImportsYamlPath(primaryPath);
+
+ if (yamlPath != null && File.Exists(yamlPath))
+ {
+ dependencies = LoadDependenciesFromYaml(yamlPath);
+ dependencyBytes = dependencies.Count * 0x10;
+ }
+ else
+ {
+ ScanBinaryDependencies(primaryData, isBigEndian, out dependencies, out dependencyBytes);
+ }
+ }
+
+ private static string GetImportsYamlPath(string primaryPath)
+ {
+ string dir = Path.GetDirectoryName(primaryPath);
+ string name = Path.GetFileNameWithoutExtension(primaryPath);
+ const string primarySuffix = "_primary";
+
+ if (name.EndsWith(primarySuffix, StringComparison.OrdinalIgnoreCase))
+ name = name.Substring(0, name.Length - primarySuffix.Length);
+
+ if (string.IsNullOrEmpty(dir) || string.IsNullOrEmpty(name))
+ return null;
+
+ return Path.Combine(dir, name + "_imports.yaml");
+ }
+
+ private static List LoadDependenciesFromYaml(string yamlPath)
+ {
+ string[] lines = File.ReadAllLines(yamlPath);
+ List list = [];
+
+ foreach (string line in lines)
+ {
+ string trimmed = line.Trim();
+
+ if (trimmed.Length == 0)
+ continue;
+
+ if (trimmed.StartsWith("#"))
+ continue;
+
+ if (trimmed[0] == '-')
+ trimmed = trimmed.Substring(1).TrimStart();
+
+ int colonIndex = trimmed.IndexOf(':');
+ if (colonIndex <= 0)
+ continue;
+
+ string offsetPart = trimmed.Substring(0, colonIndex).Trim();
+ string idPart = trimmed.Substring(colonIndex + 1).Trim();
+
+ if (!Utilities.TryParseHexUInt(offsetPart, out uint offset))
+ continue;
+
+ if (!Utilities.TryParseHexUInt(idPart, out uint id))
+ continue;
+
+ Dependency dep = new()
+ {
+ EntryPointerOffset = offset,
+ ID = id
+ };
+
+ list.Add(dep);
+ }
+
+ return list;
+ }
+
+ private static void ScanBinaryDependencies(
+ byte[] primaryData,
+ bool isBigEndian,
+ out List dependencies,
+ out int dependencyBytes)
+ {
+ dependencies = [];
+ dependencyBytes = 0;
+
+ if (primaryData.Length < 0x10)
+ return;
+
+ int pos = primaryData.Length - 0x10;
+
+ while (pos >= 0)
+ {
+ if (!TryReadDependencyEntry(primaryData, pos, isBigEndian, out uint id, out uint location))
+ break;
+
+ Dependency dep = new()
+ {
+ ID = id,
+ EntryPointerOffset = location
+ };
+
+ dependencies.Add(dep);
+ dependencyBytes += 0x10;
+ pos -= 0x10;
+ }
+
+ if (dependencies.Count > 0)
+ dependencies.Reverse();
+ }
+
+ private static bool TryReadDependencyEntry(
+ byte[] data,
+ int offset,
+ bool isBigEndian,
+ out uint id,
+ out uint location)
+ {
+ id = 0;
+ location = 0;
+
+ if (offset < 0 || offset + 0x10 > data.Length)
+ return false;
+
+ if (isBigEndian)
+ {
+ if (data[offset + 0] != 0 || data[offset + 1] != 0 ||
+ data[offset + 2] != 0 || data[offset + 3] != 0)
+ return false;
+
+ if (data[offset + 8] != 0 || data[offset + 9] != 0 ||
+ data[offset + 10] != 0 || data[offset + 11] != 0)
+ return false;
+
+ id = Utilities.ReadUInt32BE(data, offset + 4);
+ location = Utilities.ReadUInt32BE(data, offset + 12);
+ }
+ else
+ {
+ if (data[offset + 4] != 0 || data[offset + 5] != 0 ||
+ data[offset + 6] != 0 || data[offset + 7] != 0)
+ return false;
+
+ if (data[offset + 12] != 0 || data[offset + 13] != 0 ||
+ data[offset + 14] != 0 || data[offset + 15] != 0)
+ return false;
+
+ id = Utilities.ReadUInt32LE(data, offset + 0);
+ location = Utilities.ReadUInt32LE(data, offset + 8);
+ }
+
+ if (id == 0)
+ return false;
+
+ return true;
+ }
+
+ #endregion
+
+ #region Data Chunks
+
+ private static string GetDataChunkName(int index)
+ {
+ return index >= 0 && index < DataChunkNames.Length
+ ? DataChunkNames[index]
+ : "additional";
+ }
+
+ private static readonly string[] DataChunkNames =
+ {
+ "primary",
+ "secondary",
+ "tertiary"
+ };
+
+ struct DataChunkImportProperties
+ {
+ public byte DataChunkCount { get; private set; }
+ public DataChunkImportEntry[] Entries;
+
+ public EntryType EntryType
+ {
+ get => _currentEntryType;
+ set
+ {
+ _currentEntryType = value;
+ UpdateDataChunkCount();
+ UpdateEntryVisibilities();
+ }
+ }
+ private EntryType _currentEntryType = EntryType.Invalid;
+
+ public DataChunkImportProperties()
+ {
+ UpdateDataChunkCount();
+ Entries = new DataChunkImportEntry[3];
+ }
+
+ void UpdateDataChunkCount()
+ {
+ DataChunkCount = BundleEntry.GetMemoryChunkCount(_currentEntryType);
+ }
+
+ void UpdateEntryVisibilities()
+ {
+ for (byte i = 0; i < Entries.Length; i++)
+ Entries[i].Visible = DataChunkCount > i;
+ }
+ }
+
+ struct DataChunkImportEntry
+ {
+ public bool Visible
+ {
+ get => _visible;
+ set
+ {
+ _visible = value;
+ if (PathSelectorControls.Count > 0)
+ foreach (var control in PathSelectorControls)
+ control.Visible = value;
+ Path = value == false ? string.Empty : Path;
+ }
+ }
+ private bool _visible;
+
+ public List PathSelectorControls;
+ public Action? PathChanged;
+ public string Path
+ {
+ get => _path;
+ set
+ {
+ _path = value;
+ PathChanged?.Invoke();
+ }
+ }
+ private string _path;
+ }
+
+ #endregion
+ }
+}
diff --git a/BundleManager/ImportResourceForm.resx b/BundleManager/ImportResourceForm.resx
new file mode 100644
index 0000000..8b2ff64
--- /dev/null
+++ b/BundleManager/ImportResourceForm.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/BundleManager/MainForm.Designer.cs b/BundleManager/MainForm.Designer.cs
index bc120ab..8b146e4 100644
--- a/BundleManager/MainForm.Designer.cs
+++ b/BundleManager/MainForm.Designer.cs
@@ -28,342 +28,323 @@ protected override void Dispose(bool disposing)
///
private void InitializeComponent()
{
- this.components = new System.ComponentModel.Container();
+ components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
- this.mnuMain = new System.Windows.Forms.MenuStrip();
- this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.newToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator();
- this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
- this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
- this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.closeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.searchForEntryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.toolStrip1 = new System.Windows.Forms.ToolStrip();
- this.tsbNew = new System.Windows.Forms.ToolStripButton();
- this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
- this.tsbOpen = new System.Windows.Forms.ToolStripButton();
- this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
- this.tsbSave = new System.Windows.Forms.ToolStripButton();
- this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
- this.tsbSwitchMode = new System.Windows.Forms.ToolStripButton();
- this.lstEntries = new BundleManager.BetterListView();
- this.colIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
- this.colName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
- this.colID = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
- this.colType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
- this.colSize = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
- this.colPreview = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
- this.mnuLst = new System.Windows.Forms.ContextMenuStrip(this.components);
- this.previewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator();
- this.viewDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.viewDebugMenuStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.pluginToolsSeparatorItem = new System.Windows.Forms.ToolStripSeparator();
- this.mnuMain.SuspendLayout();
- this.toolStrip1.SuspendLayout();
- this.mnuLst.SuspendLayout();
- this.SuspendLayout();
+ mnuMain = new System.Windows.Forms.MenuStrip();
+ fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ newToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator();
+ openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
+ saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
+ exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ closeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ searchForEntryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ pluginToolsSeparatorItem = new System.Windows.Forms.ToolStripSeparator();
+ addResourcesToBundleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ toolStrip1 = new System.Windows.Forms.ToolStrip();
+ tsbNew = new System.Windows.Forms.ToolStripButton();
+ toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+ tsbOpen = new System.Windows.Forms.ToolStripButton();
+ toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
+ tsbSave = new System.Windows.Forms.ToolStripButton();
+ toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
+ tsbSwitchMode = new System.Windows.Forms.ToolStripButton();
+ lstEntries = new BetterListView();
+ colIndex = new System.Windows.Forms.ColumnHeader();
+ colName = new System.Windows.Forms.ColumnHeader();
+ colID = new System.Windows.Forms.ColumnHeader();
+ colType = new System.Windows.Forms.ColumnHeader();
+ colSize = new System.Windows.Forms.ColumnHeader();
+ colPreview = new System.Windows.Forms.ColumnHeader();
+ mnuLst = new System.Windows.Forms.ContextMenuStrip(components);
+ previewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator();
+ viewDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ viewDebugMenuStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ mnuMain.SuspendLayout();
+ toolStrip1.SuspendLayout();
+ mnuLst.SuspendLayout();
+ SuspendLayout();
//
// mnuMain
//
- this.mnuMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.fileToolStripMenuItem,
- this.toolsToolStripMenuItem});
- this.mnuMain.Location = new System.Drawing.Point(0, 0);
- this.mnuMain.Name = "mnuMain";
- this.mnuMain.Size = new System.Drawing.Size(784, 24);
- this.mnuMain.TabIndex = 0;
- this.mnuMain.Text = "menuStrip1";
+ mnuMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { fileToolStripMenuItem, toolsToolStripMenuItem });
+ mnuMain.Location = new System.Drawing.Point(0, 0);
+ mnuMain.Name = "mnuMain";
+ mnuMain.Padding = new System.Windows.Forms.Padding(7, 2, 0, 2);
+ mnuMain.Size = new System.Drawing.Size(915, 24);
+ mnuMain.TabIndex = 0;
+ mnuMain.Text = "menuStrip1";
//
// fileToolStripMenuItem
//
- this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.newToolStripMenuItem,
- this.toolStripMenuItem3,
- this.openToolStripMenuItem,
- this.toolStripMenuItem1,
- this.saveToolStripMenuItem,
- this.saveAsToolStripMenuItem,
- this.toolStripMenuItem2,
- this.exitToolStripMenuItem,
- this.closeToolStripMenuItem});
- this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
- this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
- this.fileToolStripMenuItem.Text = "File";
+ fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { newToolStripMenuItem, toolStripMenuItem3, openToolStripMenuItem, toolStripMenuItem1, saveToolStripMenuItem, saveAsToolStripMenuItem, toolStripMenuItem2, exitToolStripMenuItem, closeToolStripMenuItem });
+ fileToolStripMenuItem.Name = "fileToolStripMenuItem";
+ fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
+ fileToolStripMenuItem.Text = "File";
//
// newToolStripMenuItem
//
- this.newToolStripMenuItem.Image = global::BundleManager.Properties.Resources.NewDocumentHS;
- this.newToolStripMenuItem.Name = "newToolStripMenuItem";
- this.newToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N)));
- this.newToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
- this.newToolStripMenuItem.Text = "New";
- this.newToolStripMenuItem.Click += new System.EventHandler(this.newToolStripMenuItem_Click);
+ newToolStripMenuItem.Image = Properties.Resources.NewDocumentHS;
+ newToolStripMenuItem.Name = "newToolStripMenuItem";
+ newToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N;
+ newToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
+ newToolStripMenuItem.Text = "New";
+ newToolStripMenuItem.Click += newToolStripMenuItem_Click;
//
// toolStripMenuItem3
//
- this.toolStripMenuItem3.Name = "toolStripMenuItem3";
- this.toolStripMenuItem3.Size = new System.Drawing.Size(183, 6);
+ toolStripMenuItem3.Name = "toolStripMenuItem3";
+ toolStripMenuItem3.Size = new System.Drawing.Size(183, 6);
//
// openToolStripMenuItem
//
- this.openToolStripMenuItem.Image = global::BundleManager.Properties.Resources.openHS;
- this.openToolStripMenuItem.Name = "openToolStripMenuItem";
- this.openToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O)));
- this.openToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
- this.openToolStripMenuItem.Text = "Open";
- this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click);
+ openToolStripMenuItem.Image = Properties.Resources.openHS;
+ openToolStripMenuItem.Name = "openToolStripMenuItem";
+ openToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O;
+ openToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
+ openToolStripMenuItem.Text = "Open";
+ openToolStripMenuItem.Click += openToolStripMenuItem_Click;
//
// toolStripMenuItem1
//
- this.toolStripMenuItem1.Name = "toolStripMenuItem1";
- this.toolStripMenuItem1.Size = new System.Drawing.Size(183, 6);
+ toolStripMenuItem1.Name = "toolStripMenuItem1";
+ toolStripMenuItem1.Size = new System.Drawing.Size(183, 6);
//
// saveToolStripMenuItem
//
- this.saveToolStripMenuItem.Image = global::BundleManager.Properties.Resources.saveHS;
- this.saveToolStripMenuItem.Name = "saveToolStripMenuItem";
- this.saveToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S)));
- this.saveToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
- this.saveToolStripMenuItem.Text = "Save";
- this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click);
+ saveToolStripMenuItem.Image = Properties.Resources.saveHS;
+ saveToolStripMenuItem.Name = "saveToolStripMenuItem";
+ saveToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S;
+ saveToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
+ saveToolStripMenuItem.Text = "Save";
+ saveToolStripMenuItem.Click += saveToolStripMenuItem_Click;
//
// saveAsToolStripMenuItem
//
- this.saveAsToolStripMenuItem.Image = global::BundleManager.Properties.Resources.SaveAllHS;
- this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
- this.saveAsToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift)
- | System.Windows.Forms.Keys.S)));
- this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
- this.saveAsToolStripMenuItem.Text = "Save As";
- this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.saveAsToolStripMenuItem_Click);
+ saveAsToolStripMenuItem.Image = Properties.Resources.SaveAllHS;
+ saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
+ saveAsToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift | System.Windows.Forms.Keys.S;
+ saveAsToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
+ saveAsToolStripMenuItem.Text = "Save As";
+ saveAsToolStripMenuItem.Click += saveAsToolStripMenuItem_Click;
//
// toolStripMenuItem2
//
- this.toolStripMenuItem2.Name = "toolStripMenuItem2";
- this.toolStripMenuItem2.Size = new System.Drawing.Size(183, 6);
+ toolStripMenuItem2.Name = "toolStripMenuItem2";
+ toolStripMenuItem2.Size = new System.Drawing.Size(183, 6);
//
// exitToolStripMenuItem
//
- this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
- this.exitToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
- this.exitToolStripMenuItem.Text = "Exit";
- this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
+ exitToolStripMenuItem.Name = "exitToolStripMenuItem";
+ exitToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
+ exitToolStripMenuItem.Text = "Exit";
+ exitToolStripMenuItem.Click += exitToolStripMenuItem_Click;
//
// closeToolStripMenuItem
//
- this.closeToolStripMenuItem.Name = "closeToolStripMenuItem";
- this.closeToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
- this.closeToolStripMenuItem.Text = "Close";
- this.closeToolStripMenuItem.Visible = false;
- this.closeToolStripMenuItem.Click += new System.EventHandler(this.closeToolStripMenuItem_Click);
+ closeToolStripMenuItem.Name = "closeToolStripMenuItem";
+ closeToolStripMenuItem.Size = new System.Drawing.Size(186, 22);
+ closeToolStripMenuItem.Text = "Close";
+ closeToolStripMenuItem.Visible = false;
+ closeToolStripMenuItem.Click += closeToolStripMenuItem_Click;
//
// toolsToolStripMenuItem
//
- this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.searchForEntryToolStripMenuItem,
- this.pluginToolsSeparatorItem});
- this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
- this.toolsToolStripMenuItem.Size = new System.Drawing.Size(46, 20);
- this.toolsToolStripMenuItem.Text = "Tools";
+ toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { searchForEntryToolStripMenuItem, addResourcesToBundleToolStripMenuItem, pluginToolsSeparatorItem });
+ toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
+ toolsToolStripMenuItem.Size = new System.Drawing.Size(46, 20);
+ toolsToolStripMenuItem.Text = "Tools";
//
// searchForEntryToolStripMenuItem
//
- this.searchForEntryToolStripMenuItem.Name = "searchForEntryToolStripMenuItem";
- this.searchForEntryToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.F)));
- this.searchForEntryToolStripMenuItem.Size = new System.Drawing.Size(197, 22);
- this.searchForEntryToolStripMenuItem.Text = "Search for entry";
- this.searchForEntryToolStripMenuItem.Click += new System.EventHandler(this.searchForEntryToolStripMenuItem_Click);
+ searchForEntryToolStripMenuItem.Name = "searchForEntryToolStripMenuItem";
+ searchForEntryToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.F;
+ searchForEntryToolStripMenuItem.Size = new System.Drawing.Size(220, 22);
+ searchForEntryToolStripMenuItem.Text = "Search for entry";
+ searchForEntryToolStripMenuItem.Click += searchForEntryToolStripMenuItem_Click;
+ //
+ // pluginToolsSeparatorItem
+ //
+ pluginToolsSeparatorItem.Name = "pluginToolsSeparatorItem";
+ pluginToolsSeparatorItem.Size = new System.Drawing.Size(217, 6);
+ //
+ // addResourcesToBundleToolStripMenuItem
+ //
+ addResourcesToBundleToolStripMenuItem.Name = "addResourcesToBundleToolStripMenuItem";
+ addResourcesToBundleToolStripMenuItem.Size = new System.Drawing.Size(220, 22);
+ addResourcesToBundleToolStripMenuItem.Text = "Add resource(s) to bundle...";
+ addResourcesToBundleToolStripMenuItem.Click += addResourcesToBundleToolStripMenuItem_Click;
//
// toolStrip1
//
- this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.tsbNew,
- this.toolStripSeparator1,
- this.tsbOpen,
- this.toolStripSeparator2,
- this.tsbSave,
- this.toolStripSeparator3,
- this.tsbSwitchMode});
- this.toolStrip1.Location = new System.Drawing.Point(0, 24);
- this.toolStrip1.Name = "toolStrip1";
- this.toolStrip1.Size = new System.Drawing.Size(784, 25);
- this.toolStrip1.TabIndex = 1;
- this.toolStrip1.Text = "toolStrip1";
+ toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { tsbNew, toolStripSeparator1, tsbOpen, toolStripSeparator2, tsbSave, toolStripSeparator3, tsbSwitchMode });
+ toolStrip1.Location = new System.Drawing.Point(0, 24);
+ toolStrip1.Name = "toolStrip1";
+ toolStrip1.Size = new System.Drawing.Size(915, 25);
+ toolStrip1.TabIndex = 1;
+ toolStrip1.Text = "toolStrip1";
//
// tsbNew
//
- this.tsbNew.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
- this.tsbNew.Image = global::BundleManager.Properties.Resources.NewDocumentHS;
- this.tsbNew.ImageTransparentColor = System.Drawing.Color.Magenta;
- this.tsbNew.Name = "tsbNew";
- this.tsbNew.Size = new System.Drawing.Size(23, 22);
- this.tsbNew.Text = "New";
- this.tsbNew.Click += new System.EventHandler(this.tsbNew_Click);
+ tsbNew.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ tsbNew.Image = Properties.Resources.NewDocumentHS;
+ tsbNew.ImageTransparentColor = System.Drawing.Color.Magenta;
+ tsbNew.Name = "tsbNew";
+ tsbNew.Size = new System.Drawing.Size(23, 22);
+ tsbNew.Text = "New";
+ tsbNew.Click += tsbNew_Click;
//
// toolStripSeparator1
//
- this.toolStripSeparator1.Name = "toolStripSeparator1";
- this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
+ toolStripSeparator1.Name = "toolStripSeparator1";
+ toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
//
// tsbOpen
//
- this.tsbOpen.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
- this.tsbOpen.Image = global::BundleManager.Properties.Resources.openHS;
- this.tsbOpen.ImageTransparentColor = System.Drawing.Color.Magenta;
- this.tsbOpen.Name = "tsbOpen";
- this.tsbOpen.Size = new System.Drawing.Size(23, 22);
- this.tsbOpen.Text = "Open";
- this.tsbOpen.Click += new System.EventHandler(this.tsbOpen_Click);
+ tsbOpen.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ tsbOpen.Image = Properties.Resources.openHS;
+ tsbOpen.ImageTransparentColor = System.Drawing.Color.Magenta;
+ tsbOpen.Name = "tsbOpen";
+ tsbOpen.Size = new System.Drawing.Size(23, 22);
+ tsbOpen.Text = "Open";
+ tsbOpen.Click += tsbOpen_Click;
//
// toolStripSeparator2
//
- this.toolStripSeparator2.Name = "toolStripSeparator2";
- this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
+ toolStripSeparator2.Name = "toolStripSeparator2";
+ toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
//
// tsbSave
//
- this.tsbSave.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
- this.tsbSave.Image = global::BundleManager.Properties.Resources.saveHS;
- this.tsbSave.ImageTransparentColor = System.Drawing.Color.Magenta;
- this.tsbSave.Name = "tsbSave";
- this.tsbSave.Size = new System.Drawing.Size(23, 22);
- this.tsbSave.Text = "Save";
- this.tsbSave.Click += new System.EventHandler(this.tsbSave_Click);
+ tsbSave.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ tsbSave.Image = Properties.Resources.saveHS;
+ tsbSave.ImageTransparentColor = System.Drawing.Color.Magenta;
+ tsbSave.Name = "tsbSave";
+ tsbSave.Size = new System.Drawing.Size(23, 22);
+ tsbSave.Text = "Save";
+ tsbSave.Click += tsbSave_Click;
//
// toolStripSeparator3
//
- this.toolStripSeparator3.Name = "toolStripSeparator3";
- this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
+ toolStripSeparator3.Name = "toolStripSeparator3";
+ toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
//
// tsbSwitchMode
//
- this.tsbSwitchMode.Image = global::BundleManager.Properties.Resources.icon;
- this.tsbSwitchMode.ImageTransparentColor = System.Drawing.Color.Magenta;
- this.tsbSwitchMode.Name = "tsbSwitchMode";
- this.tsbSwitchMode.Size = new System.Drawing.Size(148, 22);
- this.tsbSwitchMode.Text = "Switch To Studio Mode";
- this.tsbSwitchMode.Click += new System.EventHandler(this.tsbSwitchMode_Click);
+ tsbSwitchMode.Image = Properties.Resources.icon;
+ tsbSwitchMode.ImageTransparentColor = System.Drawing.Color.Magenta;
+ tsbSwitchMode.Name = "tsbSwitchMode";
+ tsbSwitchMode.Size = new System.Drawing.Size(148, 22);
+ tsbSwitchMode.Text = "Switch To Studio Mode";
+ tsbSwitchMode.Click += tsbSwitchMode_Click;
//
// lstEntries
//
- this.lstEntries.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
- this.colIndex,
- this.colName,
- this.colID,
- this.colType,
- this.colSize,
- this.colPreview});
- this.lstEntries.ContextMenuStrip = this.mnuLst;
- this.lstEntries.Dock = System.Windows.Forms.DockStyle.Fill;
- this.lstEntries.FullRowSelect = true;
- this.lstEntries.GridLines = true;
- this.lstEntries.HideSelection = false;
- this.lstEntries.Location = new System.Drawing.Point(0, 49);
- this.lstEntries.MultiSelect = false;
- this.lstEntries.Name = "lstEntries";
- this.lstEntries.Size = new System.Drawing.Size(784, 512);
- this.lstEntries.TabIndex = 2;
- this.lstEntries.UseCompatibleStateImageBehavior = false;
- this.lstEntries.View = System.Windows.Forms.View.Details;
- this.lstEntries.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.lstEntries_ColumnClick);
- this.lstEntries.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.lstEntries_MouseDoubleClick);
+ lstEntries.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { colIndex, colName, colID, colType, colSize, colPreview });
+ lstEntries.ContextMenuStrip = mnuLst;
+ lstEntries.Dock = System.Windows.Forms.DockStyle.Fill;
+ lstEntries.FullRowSelect = true;
+ lstEntries.GridLines = true;
+ lstEntries.Location = new System.Drawing.Point(0, 49);
+ lstEntries.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ lstEntries.MultiSelect = false;
+ lstEntries.Name = "lstEntries";
+ lstEntries.Size = new System.Drawing.Size(915, 598);
+ lstEntries.TabIndex = 2;
+ lstEntries.UseCompatibleStateImageBehavior = false;
+ lstEntries.View = System.Windows.Forms.View.Details;
+ lstEntries.ColumnClick += lstEntries_ColumnClick;
+ lstEntries.MouseDoubleClick += lstEntries_MouseDoubleClick;
//
// colIndex
//
- this.colIndex.Text = "Index";
+ colIndex.Text = "Index";
+ colIndex.Width = 70;
//
// colName
//
- this.colName.Text = "Name";
- this.colName.Width = 120;
+ colName.Text = "Name";
+ colName.Width = 140;
//
// colID
//
- this.colID.Text = "ID";
- this.colID.Width = 100;
+ colID.Text = "ID";
+ colID.Width = 117;
//
// colType
//
- this.colType.Text = "Type";
- this.colType.Width = 200;
+ colType.Text = "Type";
+ colType.Width = 233;
//
// colSize
//
- this.colSize.Text = "Size";
+ colSize.Text = "Size";
+ colSize.Width = 70;
//
// colPreview
//
- this.colPreview.Text = "Preview";
- this.colPreview.Width = 300;
+ colPreview.Text = "Preview";
+ colPreview.Width = 350;
//
// mnuLst
//
- this.mnuLst.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.previewToolStripMenuItem,
- this.toolStripMenuItem4,
- this.viewDataToolStripMenuItem, this.viewDebugMenuStripMenuItem});
- this.mnuLst.Name = "mnuLst";
- this.mnuLst.Size = new System.Drawing.Size(141, 54);
+ mnuLst.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { previewToolStripMenuItem, toolStripMenuItem4, viewDataToolStripMenuItem, viewDebugMenuStripMenuItem });
+ mnuLst.Name = "mnuLst";
+ mnuLst.Size = new System.Drawing.Size(172, 76);
//
// previewToolStripMenuItem
//
- this.previewToolStripMenuItem.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.previewToolStripMenuItem.Name = "previewToolStripMenuItem";
- this.previewToolStripMenuItem.Size = new System.Drawing.Size(140, 22);
- this.previewToolStripMenuItem.Text = "Preview";
- this.previewToolStripMenuItem.Click += new System.EventHandler(this.previewToolStripMenuItem_Click);
+ previewToolStripMenuItem.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, 0);
+ previewToolStripMenuItem.Name = "previewToolStripMenuItem";
+ previewToolStripMenuItem.Size = new System.Drawing.Size(171, 22);
+ previewToolStripMenuItem.Text = "Preview";
+ previewToolStripMenuItem.Click += previewToolStripMenuItem_Click;
//
// toolStripMenuItem4
//
- this.toolStripMenuItem4.Name = "toolStripMenuItem4";
- this.toolStripMenuItem4.Size = new System.Drawing.Size(137, 6);
+ toolStripMenuItem4.Name = "toolStripMenuItem4";
+ toolStripMenuItem4.Size = new System.Drawing.Size(168, 6);
//
// viewDataToolStripMenuItem
//
- this.viewDataToolStripMenuItem.Name = "viewDataToolStripMenuItem";
- this.viewDataToolStripMenuItem.Size = new System.Drawing.Size(140, 22);
- this.viewDataToolStripMenuItem.Text = "View Header";
- this.viewDataToolStripMenuItem.Click += new System.EventHandler(this.viewDataToolStripMenuItem_Click);
+ viewDataToolStripMenuItem.Name = "viewDataToolStripMenuItem";
+ viewDataToolStripMenuItem.Size = new System.Drawing.Size(171, 22);
+ viewDataToolStripMenuItem.Text = "View Header";
+ viewDataToolStripMenuItem.Click += viewDataToolStripMenuItem_Click;
//
// viewDebugMenuStripMenuItem
//
- this.viewDebugMenuStripMenuItem.Name = "viewDebugMenuStripMenuItem";
- this.viewDebugMenuStripMenuItem.Size = new System.Drawing.Size(140, 22);
- this.viewDebugMenuStripMenuItem.Text = "View Debug Menu";
- this.viewDebugMenuStripMenuItem.Click += new System.EventHandler(this.viewDebugMenuStripMenuItem_Click);
-
- //
- // pluginToolsSeparatorItem
- //
- this.pluginToolsSeparatorItem.Name = "pluginToolsSeparatorItem";
- this.pluginToolsSeparatorItem.Size = new System.Drawing.Size(194, 6);
+ viewDebugMenuStripMenuItem.Name = "viewDebugMenuStripMenuItem";
+ viewDebugMenuStripMenuItem.Size = new System.Drawing.Size(171, 22);
+ viewDebugMenuStripMenuItem.Text = "View Debug Menu";
+ viewDebugMenuStripMenuItem.Click += viewDebugMenuStripMenuItem_Click;
//
// MainForm
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(784, 561);
- this.Controls.Add(this.lstEntries);
- this.Controls.Add(this.toolStrip1);
- this.Controls.Add(this.mnuMain);
- this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
- this.MainMenuStrip = this.mnuMain;
- this.Name = "MainForm";
- this.Text = "Burnout Bundle Manager";
- this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
- this.mnuMain.ResumeLayout(false);
- this.mnuMain.PerformLayout();
- this.toolStrip1.ResumeLayout(false);
- this.toolStrip1.PerformLayout();
- this.mnuLst.ResumeLayout(false);
- this.ResumeLayout(false);
- this.PerformLayout();
+ AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ ClientSize = new System.Drawing.Size(915, 647);
+ Controls.Add(lstEntries);
+ Controls.Add(toolStrip1);
+ Controls.Add(mnuMain);
+ Icon = (System.Drawing.Icon)resources.GetObject("$this.Icon");
+ MainMenuStrip = mnuMain;
+ Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ Name = "MainForm";
+ Text = "Burnout Bundle Manager";
+ FormClosing += MainForm_FormClosing;
+ mnuMain.ResumeLayout(false);
+ mnuMain.PerformLayout();
+ toolStrip1.ResumeLayout(false);
+ toolStrip1.PerformLayout();
+ mnuLst.ResumeLayout(false);
+ ResumeLayout(false);
+ PerformLayout();
}
@@ -403,6 +384,7 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripMenuItem searchForEntryToolStripMenuItem;
private System.Windows.Forms.ColumnHeader colName;
private System.Windows.Forms.ToolStripSeparator pluginToolsSeparatorItem;
+ private System.Windows.Forms.ToolStripMenuItem addResourcesToBundleToolStripMenuItem;
}
}
diff --git a/BundleManager/MainForm.cs b/BundleManager/MainForm.cs
index 1e0e699..76514fb 100644
--- a/BundleManager/MainForm.cs
+++ b/BundleManager/MainForm.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections;
using System.ComponentModel;
+using System.IO;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -597,6 +599,41 @@ private void searchForEntryToolStripMenuItem_Click(object sender, EventArgs e)
Search();
}
+ private void addResourcesToBundleToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ if (CurrentArchive == null)
+ return;
+
+ using OpenFileDialog ofd = new();
+
+ ofd.Title = "Please select the primary resource (or header) to import";
+ ofd.Filter = "Binary Files|*.dat;*.bin|Image Files(*.dds,*.bmp;*.gif;*.jpg;*.png;*.tif;*.tga;*.webp)|*.dds;*.bmp;*.gif;*.jpg;*.png;*.tif;*.tga;*.webp|All files (*.*)|*.*";
+
+ if (ofd.ShowDialog(this) != DialogResult.OK)
+ return;
+
+ var path = ofd.FileName;
+
+ if (string.IsNullOrEmpty(path) || !File.Exists(path))
+ return;
+
+ Match m = ResourceIDPattern().Match(Path.GetFileNameWithoutExtension(path));
+
+ if (m.Success)
+ {
+ string hex = m.Value;
+ uint id = Convert.ToUInt32(hex, 16);
+ }
+
+ using ImportResourceForm importResourceForm = new(CurrentArchive, path);
+
+ if (importResourceForm.ShowDialog(this) == DialogResult.OK)
+ {
+ CurrentArchive = importResourceForm.ResultArchive;
+ UpdateDisplay();
+ }
+ }
+
#endregion
#region Utility
@@ -761,6 +798,9 @@ public void Swap()
}
}
+ [GeneratedRegex(@"\b[0-9A-Fa-f]{8}\b")]
+ private static partial Regex ResourceIDPattern();
+
#endregion
}
}
diff --git a/BundleManager/MainForm.resx b/BundleManager/MainForm.resx
index 8f9f8c4..0e585be 100644
--- a/BundleManager/MainForm.resx
+++ b/BundleManager/MainForm.resx
@@ -1,17 +1,17 @@
-
diff --git a/BundleUtilities/Utilities.cs b/BundleUtilities/Utilities.cs
index 40b1ba1..e8174e8 100644
--- a/BundleUtilities/Utilities.cs
+++ b/BundleUtilities/Utilities.cs
@@ -4,6 +4,7 @@
using System.IO;
using System.Text;
using System.Reflection;
+using System.Globalization;
namespace BundleUtilities
{
@@ -97,5 +98,98 @@ public static void Parse(string s, bool forceHex, out T value)
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
value = (T)converter.ConvertFromString(s);
}
+
+ public static bool TryParseHexUInt(string s, out uint value)
+ {
+ try
+ {
+ Parse(s, true, out value);
+ return true;
+ }
+ catch
+ {
+ value = 0;
+ return false;
+ }
+ }
+
+ public static ushort ReadUInt16LE(byte[] data, int offset)
+ {
+ return (ushort)(data[offset]
+ | (data[offset + 1] << 8));
+ }
+
+ public static ushort ReadUInt16BE(byte[] data, int offset)
+ {
+ return (ushort)((data[offset] << 8)
+ | data[offset + 1]);
+ }
+
+ public static uint ReadUInt32LE(byte[] data, int offset)
+ {
+ return (uint)(data[offset]
+ | (data[offset + 1] << 8)
+ | (data[offset + 2] << 16)
+ | (data[offset + 3] << 24));
+ }
+
+ public static uint ReadUInt32BE(byte[] data, int offset)
+ {
+ return (uint)((data[offset] << 24)
+ | (data[offset + 1] << 16)
+ | (data[offset + 2] << 8)
+ | data[offset + 3]);
+ }
+
+ public static bool IsValidHex(string s)
+ {
+ if (string.IsNullOrWhiteSpace(s))
+ return false;
+
+ foreach (char c in s)
+ if (!Uri.IsHexDigit(c))
+ return false;
+
+ return true;
+ }
+
+ public static bool ContainsASCII(byte[] data, string value)
+ {
+ if (data.Length < value.Length)
+ return false;
+
+ byte[] pattern = Encoding.ASCII.GetBytes(value);
+
+ for (int i = 0; i <= data.Length - pattern.Length; i++)
+ {
+ bool match = true;
+
+ for (int j = 0; j < pattern.Length; j++)
+ {
+ if (data[i + j] != pattern[j])
+ {
+ match = false;
+ break;
+ }
+ }
+
+ if (match)
+ return true;
+ }
+
+ return false;
+ }
+
+ public static void CalculateResourceIDFromName(string resourceName, out uint resourceID)
+ {
+ // example: bundlemanager://.
+ resourceID = Crc32.HashEntryID(resourceName.ToLower());
+ }
+
+ public static void CalculateResourceIDFromName(string resourceName, out string resourceID)
+ {
+ CalculateResourceIDFromName(resourceName, out uint id);
+ resourceID = id.ToString("X8");
+ }
}
}
diff --git a/BurnoutImage/GameImage.cs b/BurnoutImage/GameImage.cs
index 5c894f5..b5411a9 100644
--- a/BurnoutImage/GameImage.cs
+++ b/BurnoutImage/GameImage.cs
@@ -387,24 +387,29 @@ public static PlatformType DetectPlatform(byte[] data)
int len = data.Length;
+ using MemoryStream ms = new(data);
+ using BinaryReader2 br = new(ms);
+
if (len == 0x40 || len == 0x60)
{
if (len == 0x40)
{
- using (MemoryStream ms = new(data))
- using (BinaryReader2 br = new(ms))
- {
- uint ptr = br.ReadUInt32();
- if (ptr != 0)
- return PlatformType.X360;
- }
+ ms.Seek(0x0, SeekOrigin.Begin);
+
+ if (br.ReadUInt32() != 0)
+ return PlatformType.X360;
}
return PlatformType.Remastered;
}
if (len == 0x20)
- return PlatformType.PC;
+ {
+ ms.Seek(0x4, SeekOrigin.Begin);
+
+ if (br.ReadUInt32() == 0) // Texture interface ptr
+ return PlatformType.PC;
+ }
if (len == 0x30)
return PlatformType.PS3;