diff --git a/BundleManager.sln b/BundleManager.sln
index 2f45a98..553155e 100644
--- a/BundleManager.sln
+++ b/BundleManager.sln
@@ -5,12 +5,12 @@ VisualStudioVersion = 17.5.33627.172
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BundleManager", "BundleManager\BundleManager.csproj", "{F11A0DF0-A25C-4E94-AA54-D0DD51F15A8E}"
ProjectSection(ProjectDependencies) = postProject
- {754DDA1C-8F2F-4F6C-8BC0-D6553A9A7451} = {754DDA1C-8F2F-4F6C-8BC0-D6553A9A7451}
- {7743D330-6A1A-496C-872B-E36C9EB38CE7} = {7743D330-6A1A-496C-872B-E36C9EB38CE7}
{07E80234-B95F-42DC-99A3-A89A929DDDBF} = {07E80234-B95F-42DC-99A3-A89A929DDDBF}
- {557FC08F-7ACD-44D7-AC1F-FB815A267A36} = {557FC08F-7ACD-44D7-AC1F-FB815A267A36}
- {47381AB0-8EEC-49E2-AC2F-D8AD2AC0F2DC} = {47381AB0-8EEC-49E2-AC2F-D8AD2AC0F2DC}
{20E7A1C4-AFCD-4B8A-8AEF-06B0C27E72B7} = {20E7A1C4-AFCD-4B8A-8AEF-06B0C27E72B7}
+ {47381AB0-8EEC-49E2-AC2F-D8AD2AC0F2DC} = {47381AB0-8EEC-49E2-AC2F-D8AD2AC0F2DC}
+ {557FC08F-7ACD-44D7-AC1F-FB815A267A36} = {557FC08F-7ACD-44D7-AC1F-FB815A267A36}
+ {754DDA1C-8F2F-4F6C-8BC0-D6553A9A7451} = {754DDA1C-8F2F-4F6C-8BC0-D6553A9A7451}
+ {7743D330-6A1A-496C-872B-E36C9EB38CE7} = {7743D330-6A1A-496C-872B-E36C9EB38CE7}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BundleFormat", "BundleFormat\BundleFormat.csproj", "{49B81828-760C-42DB-9FAD-96755597C871}"
@@ -55,6 +55,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorldCollisionHandler", "Wo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuaList", "LuaList\LuaList.csproj", "{D1395FDE-6A64-4D9F-9DCA-60825BD0A43C}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WheelList", "WheelList\WheelList.csproj", "{56CAC721-417D-445A-9031-DD365313C703}"
+ ProjectSection(ProjectDependencies) = postProject
+ {34471573-F236-4A7C-ABD9-C1FE4686B37E} = {34471573-F236-4A7C-ABD9-C1FE4686B37E}
+ {49B81828-760C-42DB-9FAD-96755597C871} = {49B81828-760C-42DB-9FAD-96755597C871}
+ {757F0204-F091-464D-AA42-FE8919FF73BB} = {757F0204-F091-464D-AA42-FE8919FF73BB}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -133,6 +140,10 @@ Global
{D1395FDE-6A64-4D9F-9DCA-60825BD0A43C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1395FDE-6A64-4D9F-9DCA-60825BD0A43C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1395FDE-6A64-4D9F-9DCA-60825BD0A43C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {56CAC721-417D-445A-9031-DD365313C703}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {56CAC721-417D-445A-9031-DD365313C703}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {56CAC721-417D-445A-9031-DD365313C703}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {56CAC721-417D-445A-9031-DD365313C703}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -156,6 +167,7 @@ Global
{7743D330-6A1A-496C-872B-E36C9EB38CE7} = {60F98E02-807F-4EDC-8747-147C365606B1}
{754DDA1C-8F2F-4F6C-8BC0-D6553A9A7451} = {60F98E02-807F-4EDC-8747-147C365606B1}
{D1395FDE-6A64-4D9F-9DCA-60825BD0A43C} = {60F98E02-807F-4EDC-8747-147C365606B1}
+ {56CAC721-417D-445A-9031-DD365313C703} = {60F98E02-807F-4EDC-8747-147C365606B1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {566130B7-31A2-4407-A2E1-C22E60C85C2C}
diff --git a/WheelList/Properties/Resources.Designer.cs b/WheelList/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..ffbe0b8
--- /dev/null
+++ b/WheelList/Properties/Resources.Designer.cs
@@ -0,0 +1,103 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace WheelList.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WheelList.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap AddTableHS {
+ get {
+ object obj = ResourceManager.GetObject("AddTableHS", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap CopyHS {
+ get {
+ object obj = ResourceManager.GetObject("CopyHS", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap EditTableHS {
+ get {
+ object obj = ResourceManager.GetObject("EditTableHS", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap remove_xform {
+ get {
+ object obj = ResourceManager.GetObject("remove_xform", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+ }
+}
diff --git a/WheelList/Properties/Resources.resx b/WheelList/Properties/Resources.resx
new file mode 100644
index 0000000..bf2aafb
--- /dev/null
+++ b/WheelList/Properties/Resources.resx
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+
+ ..\Resources\AddTableHS.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\CopyHS.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\EditTableHS.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\remove_xform.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
\ No newline at end of file
diff --git a/WheelList/Resources/AddTableHS.png b/WheelList/Resources/AddTableHS.png
new file mode 100644
index 0000000..71dd563
Binary files /dev/null and b/WheelList/Resources/AddTableHS.png differ
diff --git a/WheelList/Resources/CopyHS.png b/WheelList/Resources/CopyHS.png
new file mode 100644
index 0000000..cb3d871
Binary files /dev/null and b/WheelList/Resources/CopyHS.png differ
diff --git a/WheelList/Resources/EditTableHS.png b/WheelList/Resources/EditTableHS.png
new file mode 100644
index 0000000..6ebcdc5
Binary files /dev/null and b/WheelList/Resources/EditTableHS.png differ
diff --git a/WheelList/Resources/remove_xform.png b/WheelList/Resources/remove_xform.png
new file mode 100644
index 0000000..f2e38cd
Binary files /dev/null and b/WheelList/Resources/remove_xform.png differ
diff --git a/WheelList/WheelEditor.Designer.cs b/WheelList/WheelEditor.Designer.cs
new file mode 100644
index 0000000..3a4b1c8
--- /dev/null
+++ b/WheelList/WheelEditor.Designer.cs
@@ -0,0 +1,139 @@
+namespace WheelList
+{
+ partial class WheelEditor
+ {
+ ///
+ /// 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()
+ {
+ btnOk = new Button();
+ btnCancel = new Button();
+ txtIndex = new TextBox();
+ txtID = new TextBox();
+ txtName = new TextBox();
+ lblIndex = new Label();
+ lblID = new Label();
+ lblName = new Label();
+ SuspendLayout();
+ //
+ // btnOk
+ //
+ btnOk.Location = new Point(52, 117);
+ btnOk.Name = "btnOk";
+ btnOk.Size = new Size(75, 23);
+ btnOk.TabIndex = 0;
+ btnOk.Text = "OK";
+ btnOk.UseVisualStyleBackColor = true;
+ btnOk.Click += btnOk_Click;
+ //
+ // btnCancel
+ //
+ btnCancel.Location = new Point(133, 117);
+ btnCancel.Name = "btnCancel";
+ btnCancel.Size = new Size(75, 23);
+ btnCancel.TabIndex = 1;
+ btnCancel.Text = "Cancel";
+ btnCancel.UseVisualStyleBackColor = true;
+ btnCancel.Click += btnCancel_Click;
+ //
+ // txtIndex
+ //
+ txtIndex.Location = new Point(58, 23);
+ txtIndex.Name = "txtIndex";
+ txtIndex.Size = new Size(150, 23);
+ txtIndex.TabIndex = 2;
+ //
+ // txtID
+ //
+ txtID.Location = new Point(58, 53);
+ txtID.Name = "txtID";
+ txtID.Size = new Size(150, 23);
+ txtID.TabIndex = 3;
+ //
+ // txtName
+ //
+ txtName.Location = new Point(58, 83);
+ txtName.Name = "txtName";
+ txtName.Size = new Size(150, 23);
+ txtName.TabIndex = 4;
+ //
+ // lblIndex
+ //
+ lblIndex.AutoSize = true;
+ lblIndex.Location = new Point(12, 27);
+ lblIndex.Name = "lblIndex";
+ lblIndex.Size = new Size(39, 15);
+ lblIndex.TabIndex = 5;
+ lblIndex.Text = "Index:";
+ //
+ // lblID
+ //
+ lblID.AutoSize = true;
+ lblID.Location = new Point(12, 57);
+ lblID.Name = "lblID";
+ lblID.Size = new Size(21, 15);
+ lblID.TabIndex = 6;
+ lblID.Text = "ID:";
+ //
+ // lblName
+ //
+ lblName.AutoSize = true;
+ lblName.Location = new Point(11, 86);
+ lblName.Name = "lblName";
+ lblName.Size = new Size(42, 15);
+ lblName.TabIndex = 7;
+ lblName.Text = "Name:";
+ //
+ // WheelEditor
+ //
+ AutoScaleDimensions = new SizeF(7F, 15F);
+ AutoScaleMode = AutoScaleMode.Font;
+ ClientSize = new Size(234, 156);
+ Controls.Add(lblName);
+ Controls.Add(lblID);
+ Controls.Add(lblIndex);
+ Controls.Add(txtName);
+ Controls.Add(txtID);
+ Controls.Add(txtIndex);
+ Controls.Add(btnCancel);
+ Controls.Add(btnOk);
+ Name = "WheelEditor";
+ Text = "WheelEditor";
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+
+ private Button btnOk;
+ private Button btnCancel;
+ private TextBox txtIndex;
+ private TextBox txtID;
+ private TextBox txtName;
+ private Label lblIndex;
+ private Label lblID;
+ private Label lblName;
+ }
+}
\ No newline at end of file
diff --git a/WheelList/WheelEditor.cs b/WheelList/WheelEditor.cs
new file mode 100644
index 0000000..5c9ac9f
--- /dev/null
+++ b/WheelList/WheelEditor.cs
@@ -0,0 +1,122 @@
+using BundleUtilities;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Reflection.PortableExecutable;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Xml.Linq;
+
+namespace WheelList
+{
+ public partial class WheelEditor : Form
+ {
+ public delegate void Done(Wheel wheel);
+ public event Done OnDone;
+ private Wheel _wheel;
+ public Wheel Wheel
+ {
+ get
+ {
+ return _wheel;
+ }
+ set
+ {
+ _wheel = value;
+ UpdateDisplay();
+ }
+ }
+
+ public WheelEditor()
+ {
+ InitializeComponent();
+ }
+
+ private int maxIndex;
+ public WheelEditor(int max)
+ {
+ InitializeComponent();
+ maxIndex = max;
+ }
+
+ private void UpdateDisplay()
+ {
+ Text = "Edit Wheel: " + Wheel.Index;
+
+ txtIndex.Text = Wheel.Index.ToString();
+ txtID.Text = Wheel.ID.Value;
+ txtName.Text = Wheel.Name;
+ }
+
+ private Wheel GetModifiedWheel()
+ {
+ Wheel result = new Wheel(Wheel);
+
+ // Index
+ int index;
+ if (int.TryParse(txtIndex.Text, out index))
+ {
+ if (index <= maxIndex)
+ result.Index = index;
+ else
+ {
+ MessageBox.Show(this, "Index out of bounds.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return null;
+ }
+ }
+ else
+ {
+ MessageBox.Show(this, "Index is invalid.\nEnsure the value is a decimal integer.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return null;
+ }
+ // Wheel ID
+ if (txtID.Text.Trim().Length == 0)
+ {
+ MessageBox.Show(this, "ID cannot be left blank.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return null;
+ }
+ try
+ {
+ EncryptedString ID = new EncryptedString(txtID.Text);
+ result.ID = ID;
+ }
+ catch (ArgumentException e)
+ {
+ MessageBox.Show(this, e.Message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return null;
+ }
+
+ // Vehicle name
+ result.Name = txtName.Text;
+ if (result.Name.Trim().Length == 0)
+ {
+ MessageBox.Show(this, "Car name cannot be left blank.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return null;
+ }
+
+ return result;
+ }
+
+ private void btnCancel_Click(object sender, EventArgs e)
+ {
+ Close();
+ }
+
+ private void btnOk_Click(object sender, EventArgs e)
+ {
+ Wheel retWheel = GetModifiedWheel();
+ if (retWheel == null)
+ {
+ return;
+ }
+
+ OnDone?.Invoke(retWheel);
+
+ Close();
+ }
+ }
+}
diff --git a/WheelList/WheelEditor.resx b/WheelList/WheelEditor.resx
new file mode 100644
index 0000000..af32865
--- /dev/null
+++ b/WheelList/WheelEditor.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/WheelList/WheelList.csproj b/WheelList/WheelList.csproj
new file mode 100644
index 0000000..0635ab2
--- /dev/null
+++ b/WheelList/WheelList.csproj
@@ -0,0 +1,35 @@
+
+
+
+ net8.0-windows
+ enable
+ true
+ enable
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+
+
+
+
diff --git a/WheelList/WheelListData.cs b/WheelList/WheelListData.cs
new file mode 100644
index 0000000..b0f4f6a
--- /dev/null
+++ b/WheelList/WheelListData.cs
@@ -0,0 +1,120 @@
+using BundleFormat;
+using BundleUtilities;
+using PluginAPI;
+using System.Text;
+
+namespace WheelList
+{
+ public class WheelListData : IEntryData
+ {
+ public int NumWheels = 0;
+ public int EntriesPtr = 0;
+ public List Entries = [];
+
+ public WheelListData()
+ {
+
+ }
+
+ public IEntryEditor GetEditor(BundleEntry entry)
+ {
+ WheelListForm wheelList = new WheelListForm();
+ wheelList.List = this;
+ wheelList.Edit += () =>
+ {
+ Write(entry);
+ };
+
+ return wheelList;
+ }
+
+ public EntryType GetEntryType(BundleEntry entry)
+ {
+ return EntryType.WheelList;
+ }
+
+ public bool Read(BundleEntry entry, ILoader loader = null)
+ {
+ Clear();
+
+ MemoryStream ms = new MemoryStream(entry.EntryBlocks[0].Data);
+ BinaryReader2 br = new BinaryReader2(ms);
+ br.BigEndian = entry.Console;
+
+ NumWheels = br.ReadInt32();
+ EntriesPtr = br.ReadInt32();
+ br.BaseStream.Position = EntriesPtr;
+
+ for (int i = 0; i < NumWheels; i++)
+ {
+ Wheel wheel = new();
+
+ wheel.Index = i;
+ wheel.ID = new EncryptedString(br.ReadUInt64());
+ wheel.Name = Encoding.ASCII.GetString(br.ReadBytes(64));
+
+ Entries.Add(wheel);
+ }
+
+ br.Close();
+
+ return true;
+ }
+
+ public bool Write(BundleEntry entry)
+ {
+ MemoryStream ms = new MemoryStream();
+ BinaryWriter2 bw = new BinaryWriter2(ms);
+ bw.BigEndian = entry.Console;
+
+ bw.Write(Entries.Count);
+ bw.Write(EntriesPtr);
+ bw.BaseStream.Position = EntriesPtr;
+
+ for (int i = 0; i < Entries.Count; i++)
+ {
+ Wheel wheel = Entries[i];
+
+ bw.Write(wheel.ID.Encrypted);
+ bw.WriteLenString(wheel.Name, 64);
+ }
+
+ bw.Align(0x10);
+
+ bw.Flush();
+ byte[] data = ms.ToArray();
+ bw.Close();
+
+ entry.EntryBlocks[0].Data = data;
+ entry.Dirty = true;
+
+ return true;
+ }
+
+ private void Clear()
+ {
+ NumWheels = default;
+ EntriesPtr = default;
+ Entries.Clear();
+ }
+ }
+
+ public class Wheel
+ {
+ public int Index = -1;
+ public EncryptedString ID = new(0);
+ public string Name = new("");
+
+ public Wheel()
+ {
+
+ }
+
+ public Wheel(Wheel copy)
+ {
+ Index = copy.Index;
+ ID = copy.ID;
+ Name = copy.Name;
+ }
+ }
+}
diff --git a/WheelList/WheelListForm.Designer.cs b/WheelList/WheelListForm.Designer.cs
new file mode 100644
index 0000000..c001d28
--- /dev/null
+++ b/WheelList/WheelListForm.Designer.cs
@@ -0,0 +1,222 @@
+namespace WheelList
+{
+ partial class WheelListForm
+ {
+ ///
+ /// 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()
+ {
+ menuStrip1 = new MenuStrip();
+ itemsToolStripMenuItem = new ToolStripMenuItem();
+ addItemToolStripMenuItem = new ToolStripMenuItem();
+ copyItemToolStripMenuItem = new ToolStripMenuItem();
+ deleteItemToolStripMenuItem = new ToolStripMenuItem();
+ toolStrip1 = new ToolStrip();
+ tsbAddItem = new ToolStripButton();
+ tsbCopyItem = new ToolStripButton();
+ tsbDeleteItem = new ToolStripButton();
+ lstWheels = new ListView();
+ Index = new ColumnHeader();
+ ID = new ColumnHeader();
+ WheelName = new ColumnHeader();
+ stsMain = new StatusStrip();
+ stlStatusLabel = new ToolStripStatusLabel();
+ menuStrip1.SuspendLayout();
+ toolStrip1.SuspendLayout();
+ stsMain.SuspendLayout();
+ SuspendLayout();
+ //
+ // menuStrip1
+ //
+ menuStrip1.Items.AddRange(new ToolStripItem[] { itemsToolStripMenuItem });
+ menuStrip1.Location = new Point(0, 0);
+ menuStrip1.Name = "menuStrip1";
+ menuStrip1.Size = new Size(261, 24);
+ menuStrip1.TabIndex = 0;
+ menuStrip1.Text = "menuStrip1";
+ //
+ // itemsToolStripMenuItem
+ //
+ itemsToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { addItemToolStripMenuItem, copyItemToolStripMenuItem, deleteItemToolStripMenuItem });
+ itemsToolStripMenuItem.Name = "itemsToolStripMenuItem";
+ itemsToolStripMenuItem.Size = new Size(48, 20);
+ itemsToolStripMenuItem.Text = "Items";
+ //
+ // addItemToolStripMenuItem
+ //
+ addItemToolStripMenuItem.Image = Properties.Resources.AddTableHS;
+ addItemToolStripMenuItem.Name = "addItemToolStripMenuItem";
+ addItemToolStripMenuItem.Size = new Size(134, 22);
+ addItemToolStripMenuItem.Text = "Add Item";
+ addItemToolStripMenuItem.Click += addItemToolStripMenuItem_Click;
+ //
+ // copyItemToolStripMenuItem
+ //
+ copyItemToolStripMenuItem.Enabled = false;
+ copyItemToolStripMenuItem.Image = Properties.Resources.CopyHS;
+ copyItemToolStripMenuItem.Name = "copyItemToolStripMenuItem";
+ copyItemToolStripMenuItem.Size = new Size(134, 22);
+ copyItemToolStripMenuItem.Text = "Copy Item";
+ copyItemToolStripMenuItem.Click += copyItemToolStripMenuItem_Click;
+ //
+ // deleteItemToolStripMenuItem
+ //
+ deleteItemToolStripMenuItem.Enabled = false;
+ deleteItemToolStripMenuItem.Image = Properties.Resources.remove_xform;
+ deleteItemToolStripMenuItem.Name = "deleteItemToolStripMenuItem";
+ deleteItemToolStripMenuItem.Size = new Size(134, 22);
+ deleteItemToolStripMenuItem.Text = "Delete Item";
+ deleteItemToolStripMenuItem.Click += deleteItemToolStripMenuItem_Click;
+ //
+ // toolStrip1
+ //
+ toolStrip1.Items.AddRange(new ToolStripItem[] { tsbAddItem, tsbCopyItem, tsbDeleteItem });
+ toolStrip1.Location = new Point(0, 24);
+ toolStrip1.Name = "toolStrip1";
+ toolStrip1.Size = new Size(261, 25);
+ toolStrip1.TabIndex = 1;
+ toolStrip1.Text = "toolStrip1";
+ //
+ // tsbAddItem
+ //
+ tsbAddItem.DisplayStyle = ToolStripItemDisplayStyle.Image;
+ tsbAddItem.Image = Properties.Resources.AddTableHS;
+ tsbAddItem.ImageTransparentColor = Color.Magenta;
+ tsbAddItem.Name = "tsbAddItem";
+ tsbAddItem.Size = new Size(23, 22);
+ tsbAddItem.Text = "toolStripButton1";
+ tsbAddItem.ToolTipText = "Add Item";
+ tsbAddItem.Click += tsbAddItem_Click;
+ //
+ // tsbCopyItem
+ //
+ tsbCopyItem.DisplayStyle = ToolStripItemDisplayStyle.Image;
+ tsbCopyItem.Enabled = false;
+ tsbCopyItem.Image = Properties.Resources.CopyHS;
+ tsbCopyItem.ImageTransparentColor = Color.Magenta;
+ tsbCopyItem.Name = "tsbCopyItem";
+ tsbCopyItem.Size = new Size(23, 22);
+ tsbCopyItem.Text = "toolStripButton2";
+ tsbCopyItem.ToolTipText = "Copy Item";
+ tsbCopyItem.Click += tsbCopyItem_Click;
+ //
+ // tsbDeleteItem
+ //
+ tsbDeleteItem.DisplayStyle = ToolStripItemDisplayStyle.Image;
+ tsbDeleteItem.Enabled = false;
+ tsbDeleteItem.Image = Properties.Resources.remove_xform;
+ tsbDeleteItem.ImageTransparentColor = Color.Magenta;
+ tsbDeleteItem.Name = "tsbDeleteItem";
+ tsbDeleteItem.Size = new Size(23, 22);
+ tsbDeleteItem.Text = "toolStripButton3";
+ tsbDeleteItem.ToolTipText = "Delete Item";
+ tsbDeleteItem.Click += tsbDeleteItem_Click;
+ //
+ // lstWheels
+ //
+ lstWheels.Columns.AddRange(new ColumnHeader[] { Index, ID, WheelName });
+ lstWheels.Dock = DockStyle.Fill;
+ lstWheels.FullRowSelect = true;
+ lstWheels.GridLines = true;
+ lstWheels.Location = new Point(0, 49);
+ lstWheels.Name = "lstWheels";
+ lstWheels.Size = new Size(261, 370);
+ lstWheels.TabIndex = 2;
+ lstWheels.UseCompatibleStateImageBehavior = false;
+ lstWheels.View = View.Details;
+ lstWheels.ColumnClick += lstWheels_ColumnClick;
+ lstWheels.SelectedIndexChanged += lstWheels_SelectedIndexChanged;
+ lstWheels.MouseDoubleClick += lstWheels_MouseDoubleClick;
+ //
+ // Index
+ //
+ Index.Text = "Index";
+ Index.Width = 48;
+ //
+ // ID
+ //
+ ID.Text = "ID";
+ ID.Width = 64;
+ //
+ // WheelName
+ //
+ WheelName.Text = "Name";
+ WheelName.Width = 128;
+ //
+ // stsMain
+ //
+ stsMain.Items.AddRange(new ToolStripItem[] { stlStatusLabel });
+ stsMain.Location = new Point(0, 419);
+ stsMain.Name = "stsMain";
+ stsMain.Size = new Size(261, 22);
+ stsMain.TabIndex = 3;
+ stsMain.Text = "statusStrip1";
+ //
+ // stlStatusLabel
+ //
+ stlStatusLabel.Name = "stlStatusLabel";
+ stlStatusLabel.Size = new Size(0, 17);
+ //
+ // WheelListForm
+ //
+ AutoScaleDimensions = new SizeF(7F, 15F);
+ AutoScaleMode = AutoScaleMode.Font;
+ ClientSize = new Size(261, 441);
+ Controls.Add(lstWheels);
+ Controls.Add(stsMain);
+ Controls.Add(toolStrip1);
+ Controls.Add(menuStrip1);
+ MainMenuStrip = menuStrip1;
+ Name = "WheelListForm";
+ Text = "Wheel List Viewer";
+ menuStrip1.ResumeLayout(false);
+ menuStrip1.PerformLayout();
+ toolStrip1.ResumeLayout(false);
+ toolStrip1.PerformLayout();
+ stsMain.ResumeLayout(false);
+ stsMain.PerformLayout();
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+
+ private MenuStrip menuStrip1;
+ private ToolStripMenuItem itemsToolStripMenuItem;
+ private ToolStripMenuItem addItemToolStripMenuItem;
+ private ToolStripMenuItem copyItemToolStripMenuItem;
+ private ToolStripMenuItem deleteItemToolStripMenuItem;
+ private ToolStrip toolStrip1;
+ private ToolStripButton tsbAddItem;
+ private ToolStripButton tsbCopyItem;
+ private ToolStripButton tsbDeleteItem;
+ private ListView lstWheels;
+ private StatusStrip stsMain;
+ private ColumnHeader Index;
+ private ColumnHeader ID;
+ private ColumnHeader WheelName;
+ private ToolStripStatusLabel stlStatusLabel;
+ }
+}
diff --git a/WheelList/WheelListForm.cs b/WheelList/WheelListForm.cs
new file mode 100644
index 0000000..70b195e
--- /dev/null
+++ b/WheelList/WheelListForm.cs
@@ -0,0 +1,290 @@
+using BundleUtilities;
+using PluginAPI;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace WheelList
+{
+ public partial class WheelListForm : Form, IEntryEditor
+ {
+ public delegate void OnEdit();
+ public event OnEdit Edit;
+
+ private WheelListData _list;
+ public WheelListData List
+ {
+ get => _list;
+ set
+ {
+ _list = value;
+ UpdateDisplay();
+ }
+ }
+
+ public WheelListForm()
+ {
+ InitializeComponent();
+ }
+
+ private void UpdateDisplay()
+ {
+ lstWheels.Items.Clear();
+
+ if (List == null)
+ return;
+
+ for (int i = 0; i < List.Entries.Count; i++)
+ {
+ Wheel wheel = List.Entries[i];
+
+ string[] value = {
+ wheel.Index.ToString(),
+ wheel.ID.Value,
+ wheel.Name
+ };
+ lstWheels.Items.Add(new ListViewItem(value));
+ }
+
+ lstWheels.ListViewItemSorter = new WheelSorter(0);
+ lstWheels.Sort();
+
+ stlStatusLabel.Text = "";
+ copyItemToolStripMenuItem.Enabled = false;
+ deleteItemToolStripMenuItem.Enabled = false;
+ tsbCopyItem.Enabled = false;
+ tsbDeleteItem.Enabled = false;
+ }
+
+ private void EditSelectedEntry()
+ {
+ if (lstWheels.SelectedItems.Count > 1)
+ return;
+ if (List == null || lstWheels.SelectedIndices.Count <= 0)
+ return;
+
+ if (!int.TryParse(lstWheels.SelectedItems[0].Text, out int index))
+ return;
+ Wheel wheel = List.Entries[index];
+
+ WheelEditor editor = new WheelEditor(List.Entries.Count - 1);
+ editor.Wheel = wheel;
+ editor.OnDone += Editor_OnDone;
+ editor.ShowDialog(this);
+ }
+
+ private void AddItem()
+ {
+ if (List == null)
+ return;
+ Wheel wheel = new();
+ wheel.Index = List.Entries.Count;
+ wheel.ID = new EncryptedString("");
+ wheel.Name = "";
+
+ WheelEditor editor = new WheelEditor(List.Entries.Count);
+ editor.Wheel = wheel;
+ editor.OnDone += Editor_OnDone1; ;
+ editor.ShowDialog(this);
+ }
+
+ private void CopyItem()
+ {
+ if (List == null || lstWheels.SelectedItems.Count != 1
+ || lstWheels.SelectedIndices.Count <= 0)
+ return;
+
+ if (!int.TryParse(lstWheels.SelectedItems[0].Text, out int index))
+ return;
+ Wheel wheel = new Wheel(List.Entries[index]);
+ wheel.Index = List.Entries.Count;
+
+ WheelEditor editor = new WheelEditor(List.Entries.Count);
+ editor.Wheel = wheel;
+ editor.OnDone += Editor_OnDone1;
+ editor.ShowDialog(this);
+ }
+
+ private void DeleteItem()
+ {
+ if (List == null || lstWheels.SelectedItems.Count != 1
+ || lstWheels.SelectedIndices.Count <= 0)
+ return;
+
+ if (!int.TryParse(lstWheels.SelectedItems[0].Text, out int index))
+ return;
+ List.Entries.RemoveAt(index);
+ for (int i = index; i < List.Entries.Count; ++i)
+ List.Entries[i].Index--;
+
+ Edit?.Invoke();
+ UpdateDisplay();
+ }
+
+ private void Editor_OnDone1(Wheel wheel)
+ {
+ // Insert if not at end, else add
+ if (wheel.Index != List.Entries.Count)
+ {
+ List.Entries.Insert(wheel.Index, wheel);
+
+ for (int i = 0; i < List.Entries.Count; ++i)
+ List.Entries[i].Index = i;
+ }
+ else
+ {
+ List.Entries.Add(wheel);
+ }
+
+ Edit?.Invoke();
+ UpdateDisplay();
+ }
+
+ private void Editor_OnDone(Wheel wheel)
+ {
+ // If the index has changed, edit the list
+ int oldIndex = int.Parse(lstWheels.SelectedItems[0].Text); // Tried in EditSelectedEntry()
+ if (oldIndex != wheel.Index)
+ {
+ Wheel old = List.Entries[oldIndex];
+ List.Entries.RemoveAt(oldIndex);
+ List.Entries.Insert(wheel.Index, old);
+
+ for (int i = 0; i < List.Entries.Count; ++i)
+ List.Entries[i].Index = i;
+ }
+
+ // Edit the wheel
+ List.Entries[wheel.Index] = wheel;
+ Edit?.Invoke();
+ UpdateDisplay();
+ }
+
+ private void lstWheels_MouseDoubleClick(object sender, MouseEventArgs e)
+ {
+ EditSelectedEntry();
+ }
+
+ private void lstWheels_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ stlStatusLabel.Text = lstWheels.SelectedItems.Count + " Item(s) Selected";
+ copyItemToolStripMenuItem.Enabled = true;
+ deleteItemToolStripMenuItem.Enabled = true;
+ tsbCopyItem.Enabled = true;
+ tsbDeleteItem.Enabled = true;
+ }
+
+ private void lstWheels_ColumnClick(object sender, ColumnClickEventArgs e)
+ {
+ int column = e.Column;
+
+ bool direction = false;
+
+ if (lstWheels.ListViewItemSorter is WheelSorter sorter)
+ {
+ if (sorter.Column == column)
+ {
+ sorter.Swap();
+ lstWheels.Sort();
+ return;
+ }
+ direction = sorter.Direction;
+ }
+
+ WheelSorter newSorter = new WheelSorter(column)
+ {
+ Direction = !direction
+ };
+ lstWheels.ListViewItemSorter = newSorter;
+ lstWheels.Sort();
+ }
+
+ private class WheelSorter : IComparer
+ {
+ public readonly int Column;
+ public bool Direction;
+
+ public WheelSorter(int column)
+ {
+ Column = column;
+ Direction = false;
+ }
+
+ public int Compare(object x, object y)
+ {
+ ListViewItem itemX = (ListViewItem)x;
+ ListViewItem itemY = (ListViewItem)y;
+
+ if (Column > itemX.SubItems.Count || Column > itemY.SubItems.Count)
+ {
+ if (Direction)
+ return -1;
+ return 1;
+ }
+
+ string iX = itemX.SubItems[Column].Text;
+ string iY = itemY.SubItems[Column].Text;
+
+
+ if (int.TryParse(iX, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out int iXint))
+ {
+ if (int.TryParse(iY, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out int iYint))
+ {
+ int val2 = iXint.CompareTo(iYint);
+ if (this.Direction)
+ return val2 * -1;
+ return val2;
+ }
+ }
+
+ int val = string.CompareOrdinal(iX, iY);
+ if (Direction)
+ return val * -1;
+ return val;
+ }
+
+ public void Swap()
+ {
+ Direction = !Direction;
+ }
+ }
+
+ private void addItemToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ AddItem();
+ }
+
+ private void tsbAddItem_Click(object sender, EventArgs e)
+ {
+ AddItem();
+ }
+
+ private void copyItemToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ CopyItem();
+ }
+
+ private void tsbCopyItem_Click(object sender, EventArgs e)
+ {
+ CopyItem();
+ }
+
+ private void deleteItemToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ DeleteItem();
+ }
+
+ private void tsbDeleteItem_Click(object sender, EventArgs e)
+ {
+ DeleteItem();
+ }
+ }
+}
diff --git a/WheelList/WheelListForm.resx b/WheelList/WheelListForm.resx
new file mode 100644
index 0000000..ca320dd
--- /dev/null
+++ b/WheelList/WheelListForm.resx
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+ 132, 17
+
+
+ 237, 17
+
+
\ No newline at end of file
diff --git a/WheelList/WheelListPlugin.cs b/WheelList/WheelListPlugin.cs
new file mode 100644
index 0000000..ddec0ea
--- /dev/null
+++ b/WheelList/WheelListPlugin.cs
@@ -0,0 +1,23 @@
+using BundleFormat;
+using PluginAPI;
+
+namespace WheelList
+{
+ public class WheelListPlugin : Plugin
+ {
+ public override void Init()
+ {
+ EntryTypeRegistry.Register(EntryType.WheelList, new WheelListData());
+ }
+
+ public override string GetID()
+ {
+ return "wheellistplugin";
+ }
+
+ public override string GetName()
+ {
+ return "WheelList Resource Handler";
+ }
+ }
+}