From bf8787efc9270ce7fb34baad322ea429ce687e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Fri, 16 Dec 2022 17:35:32 +0100 Subject: [PATCH 01/45] Fix CPlugSurface.Box --- Src/GBX.NET/Engines/Plug/CPlugSurface.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Src/GBX.NET/Engines/Plug/CPlugSurface.cs b/Src/GBX.NET/Engines/Plug/CPlugSurface.cs index 684f47ed6..3bb6b388d 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugSurface.cs +++ b/Src/GBX.NET/Engines/Plug/CPlugSurface.cs @@ -222,18 +222,15 @@ public interface ISurf : IReadableWritable public class Box : ISurf { private NET.Box transform; - private short u02; public int Id => 6; public Vec3? U01 { get; set; } public NET.Box Transform { get => transform; set => transform = value; } - public short U02 { get => u02; set => u02 = value; } public void ReadWrite(GameBoxReaderWriter rw, int version = 0) { rw.Box(ref transform); - rw.Int16(ref u02); } } From 113cd5b367cb9ceffc2856445ec3ca67268bc573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Fri, 16 Dec 2022 20:06:18 +0100 Subject: [PATCH 02/45] Fix all TMUF solids (but kinda weirdly) --- Src/GBX.NET/Engines/Plug/CPlugVisual3D.cs | 38 ++++++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/Src/GBX.NET/Engines/Plug/CPlugVisual3D.cs b/Src/GBX.NET/Engines/Plug/CPlugVisual3D.cs index 72c84809c..cb298e385 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugVisual3D.cs +++ b/Src/GBX.NET/Engines/Plug/CPlugVisual3D.cs @@ -96,8 +96,10 @@ public override void Read(CPlugVisual3D n, GameBoxReader r) { var u01 = !n.IsFlagBitSet(22) || n.HasVertexNormals; var u02 = !n.IsFlagBitSet(22) || n.IsFlagBitSet(8); + var u03 = n.IsFlagBitSet(20); + var u04 = n.IsFlagBitSet(21); + var isSprite = n is CPlugVisualSprite; - // Console.WriteLine("numBytesPerVertex={0}", numBytesPerVertex); n.vertices = r.ReadArray(n.Count, r => { var pos = r.ReadVec3(); @@ -108,7 +110,7 @@ public override void Read(CPlugVisual3D n, GameBoxReader r) if (u01) { - if (n.IsFlagBitSet(20)) + if (u03) { vertU01 = r.ReadInt32(); } @@ -120,7 +122,7 @@ public override void Read(CPlugVisual3D n, GameBoxReader r) if (u02) { - if (n.IsFlagBitSet(21)) + if (u04) { vertU03 = r.ReadInt32(); } @@ -129,6 +131,15 @@ public override void Read(CPlugVisual3D n, GameBoxReader r) vertU04 = r.ReadVec4(); } } + + var vertU05 = default(float?); + var vertU06 = default(int?); + + if (isSprite) + { + vertU05 = r.ReadSingle(); + vertU06 = r.ReadInt32(); + } return new Vertex { @@ -136,7 +147,9 @@ public override void Read(CPlugVisual3D n, GameBoxReader r) U04 = vertU01, U05 = vertU02, U06 = vertU03, - U07 = vertU04 + U07 = vertU04, + U08 = vertU05, + U09 = vertU06, }; }); @@ -148,7 +161,10 @@ public override void Write(CPlugVisual3D n, GameBoxWriter w) { var u01 = !n.IsFlagBitSet(22) || n.HasVertexNormals; var u02 = !n.IsFlagBitSet(22) || n.IsFlagBitSet(8); - + var u03 = n.IsFlagBitSet(20); + var u04 = n.IsFlagBitSet(21); + var isSprite = n is CPlugVisualSprite; + for (var i = 0; i < n.Count; i++) { var v = n.vertices[i]; @@ -156,7 +172,7 @@ public override void Write(CPlugVisual3D n, GameBoxWriter w) if (u01) { - if (n.IsFlagBitSet(20)) + if (u03) { w.Write(v.U04.GetValueOrDefault()); } @@ -168,7 +184,7 @@ public override void Write(CPlugVisual3D n, GameBoxWriter w) if (u02) { - if (n.IsFlagBitSet(21)) + if (u04) { w.Write(v.U06.GetValueOrDefault()); } @@ -177,6 +193,12 @@ public override void Write(CPlugVisual3D n, GameBoxWriter w) w.Write(v.U07.GetValueOrDefault()); } } + + if (isSprite) + { + w.Write(v.U08.GetValueOrDefault()); + w.Write(v.U09.GetValueOrDefault()); + } } WriteTangents(w, Tangents1Count, Tangents1); @@ -204,7 +226,7 @@ private static void WriteTangents(GameBoxWriter w, int tangentsCount, byte[]? ta } } - public readonly record struct Vertex(Vec3 Position, Vec3? Normal, Vec3? U02, float? U03, int? U04, Vec3? U05, int? U06, Vec4? U07) + public readonly record struct Vertex(Vec3 Position, Vec3? Normal, Vec3? U02, float? U03, int? U04, Vec3? U05, int? U06, Vec4? U07, float? U08, int? U09) { public override string ToString() => Position.ToString(); } From c894c8565158273a56e7326f249864b584a8b926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Fri, 16 Dec 2022 20:44:50 +0100 Subject: [PATCH 03/45] Fix NodeRef writing --- Src/GBX.NET/GameBoxReaderWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/GBX.NET/GameBoxReaderWriter.cs b/Src/GBX.NET/GameBoxReaderWriter.cs index e50df1ea9..a82d693d0 100644 --- a/Src/GBX.NET/GameBoxReaderWriter.cs +++ b/Src/GBX.NET/GameBoxReaderWriter.cs @@ -2175,7 +2175,7 @@ public void DictionaryNode(ref IDictionary? diction public Node? NodeRef(Node? variable, ref GameBoxRefTable.File? nodeRefFile) { if (Reader is not null) variable = Reader.ReadNodeRef(out nodeRefFile); - if (Writer is not null) Writer.Write(variable); + if (Writer is not null) Writer.Write(variable, nodeRefFile); return variable; } From ac9e2fbfcc8b3943cd27cc54f91331f0c25f3c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Fri, 16 Dec 2022 22:29:34 +0100 Subject: [PATCH 04/45] Add GetDirectoryName --- Src/GBX.NET.PAK/NadeoPakFile.cs | 44 ++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/Src/GBX.NET.PAK/NadeoPakFile.cs b/Src/GBX.NET.PAK/NadeoPakFile.cs index 75b65ed09..aaf56e5a6 100644 --- a/Src/GBX.NET.PAK/NadeoPakFile.cs +++ b/Src/GBX.NET.PAK/NadeoPakFile.cs @@ -181,26 +181,40 @@ public Stream Open() public string GetFullFileName() { - if (Folder == null) return Name; - - var currentParent = Folder; - var folders = new List - { - Name - }; + return Path.Combine(GetDirectoryName(), Name); + } - while (currentParent != null) - { - folders.Insert(0, currentParent.Name); - currentParent = currentParent.Parent; - } + /// + /// Gets the directory name (relative to the PAK file), also including directories in . + /// + /// A full directory name. + public string GetFullDirectoryName() + { + return Path.GetDirectoryName(GetFullFileName()) ?? ""; + } - return Path.Combine(folders.ToArray()); + /// + /// Gets the directory name (relative to the PAK file), not part of the itself. + /// + /// + public string GetDirectoryName() + { +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER + return string.Join(Path.DirectorySeparatorChar, GetParentDirectories().Reverse()); +#else + return Path.Combine(GetParentDirectories().Reverse().ToArray()); +#endif } - public string? GetFullDirectoryName() + private IEnumerable GetParentDirectories() { - return Path.GetDirectoryName(GetFullFileName()); + var currentParent = Folder; + + while (currentParent is not null) + { + yield return currentParent.Name; + currentParent = currentParent.Parent; + } } public string GetFileName() From 13ffccfc6fb63ef3eb9e33f66629affcdb79e2ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Fri, 16 Dec 2022 22:32:50 +0100 Subject: [PATCH 05/45] Add Motion to CSceneObject --- Src/GBX.NET/Engines/Scene/CSceneObject.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Src/GBX.NET/Engines/Scene/CSceneObject.cs b/Src/GBX.NET/Engines/Scene/CSceneObject.cs index e00c1c758..e0797f926 100644 --- a/Src/GBX.NET/Engines/Scene/CSceneObject.cs +++ b/Src/GBX.NET/Engines/Scene/CSceneObject.cs @@ -4,6 +4,12 @@ [Node(0x0A005000)] public abstract class CSceneObject : CMwNod { + private CMotion? motion; + + [NodeMember(ExactlyNamed = true)] + [AppliedWithChunk] + public CMotion? Motion { get => motion; set => motion = value; } + internal CSceneObject() { @@ -33,7 +39,7 @@ public class Chunk0A005002 : Chunk public override void ReadWrite(CSceneObject n, GameBoxReaderWriter rw) { - rw.Boolean(ref U01); + rw.Boolean(ref U01); // same as 0x004 U01 } } @@ -43,11 +49,9 @@ public override void ReadWrite(CSceneObject n, GameBoxReaderWriter rw) [Chunk(0x0A005003)] public class Chunk0A005003 : Chunk { - public CMwNod? U01; - public override void ReadWrite(CSceneObject n, GameBoxReaderWriter rw) { - rw.NodeRef(ref U01); // CMotion? + rw.NodeRef(ref n.motion); } } @@ -61,7 +65,7 @@ public class Chunk0A005004 : Chunk public override void ReadWrite(CSceneObject n, GameBoxReaderWriter rw) { - rw.Int32(ref U01); + rw.Int32(ref U01); // same as 0x002 U01 } } } From 9e20f579cbf0f5e77e600e3137dec0e3e7f69251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sat, 17 Dec 2022 04:09:12 +0100 Subject: [PATCH 06/45] Expose Shader files --- Src/GBX.NET/Engines/Plug/CPlugMaterial.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Src/GBX.NET/Engines/Plug/CPlugMaterial.cs b/Src/GBX.NET/Engines/Plug/CPlugMaterial.cs index 840b585eb..7f4492321 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugMaterial.cs +++ b/Src/GBX.NET/Engines/Plug/CPlugMaterial.cs @@ -200,18 +200,21 @@ public CPlugShader? Shader1 get => shader1 = node?.GetNodeFromRefTable(shader1, shader1File) as CPlugShader; set => shader1 = value; } + public GameBoxRefTable.File? Shader1File { get => shader1File; set => shader1File = value; } public CPlugShader? Shader2 { get => shader2 = node?.GetNodeFromRefTable(shader2, shader2File) as CPlugShader; set => shader2 = value; } + public GameBoxRefTable.File? Shader2File { get => shader2File; set => shader2File = value; } public CPlugShader? Shader3 { get => shader3 = node?.GetNodeFromRefTable(shader3, shader3File) as CPlugShader; set => shader3 = value; } + public GameBoxRefTable.File? Shader3File { get => shader3File; set => shader3File = value; } public void ReadWrite(GameBoxReaderWriter rw, GameBox? gbx, int version = 0) { From 2e4a635f455075c8d5fc1f7aa2e694746718933a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Wed, 21 Dec 2022 03:42:28 +0100 Subject: [PATCH 07/45] Fix some issues with CPlugSurface --- Src/GBX.NET/Engines/Plug/CPlugSurface.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/GBX.NET/Engines/Plug/CPlugSurface.cs b/Src/GBX.NET/Engines/Plug/CPlugSurface.cs index 3bb6b388d..5dc39a068 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugSurface.cs +++ b/Src/GBX.NET/Engines/Plug/CPlugSurface.cs @@ -124,7 +124,7 @@ public override void ReadWrite(CPlugSurface n, GameBoxReaderWriter rw) rw.NodeRef(ref n.geom); - rw.ArrayArchive(n.materials); + rw.ArrayArchiveWithGbx(ref n.materials); } } @@ -202,7 +202,7 @@ public void ReadWrite(GameBoxReaderWriter rw, GameBox? gbx, int version = 0) public void ReadWrite(GameBoxReaderWriter rw, int version = 0) { - if (rw.Boolean(material is not null)) + if (rw.Boolean(material is not null || materialFile is not null)) { rw.NodeRef(ref material, ref materialFile); } From f3ff9020df747aa0687257e99050fa0b773c5e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Wed, 21 Dec 2022 03:44:00 +0100 Subject: [PATCH 08/45] Make ref table file writing more flexible --- Src/GBX.NET/GameBoxWriter.cs | 31 +++++++++---------------------- Src/GBX.NET/GbxState.cs | 2 ++ 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Src/GBX.NET/GameBoxWriter.cs b/Src/GBX.NET/GameBoxWriter.cs index f3b05ff1b..8a0e81e5e 100644 --- a/Src/GBX.NET/GameBoxWriter.cs +++ b/Src/GBX.NET/GameBoxWriter.cs @@ -371,28 +371,20 @@ public void Write(Node? node, GameBoxRefTable.File? nodeFile = null) { if (nodeFile is not null) { - var nodeFileIndex = nodeFile.NodeIndex; - var alreadyAdded = false; - - while (AuxNodes.TryGetValue(nodeFileIndex, out Node? alreadyAddedNode)) + foreach (var pair in State.ExtAuxNodes) { - if (alreadyAddedNode is null || node == alreadyAddedNode) + if (pair.Value == nodeFile) { - alreadyAdded = true; - break; + Write(pair.Key + 1); + return; } - - nodeFileIndex++; } - nodeFile.NodeIndex = nodeFileIndex; + var i = AuxNodes.Count + State.ExtAuxNodes.Count; + nodeFile.NodeIndex = i; + State.ExtAuxNodes[i] = nodeFile; - Write(nodeFileIndex + 1); - - if (!alreadyAdded) - { - AuxNodes.Add(nodeFileIndex, null); - } + Write(i + 1); return; } @@ -424,12 +416,7 @@ public void Write(Node? node, GameBoxRefTable.File? nodeFile = null) } } - var index = AuxNodes.Count; - - while (AuxNodes.ContainsKey(index)) - { - index++; - } + var index = AuxNodes.Count + State.ExtAuxNodes.Count; AuxNodes.Add(index, node); diff --git a/Src/GBX.NET/GbxState.cs b/Src/GBX.NET/GbxState.cs index 9ea7f7ccc..1991df608 100644 --- a/Src/GBX.NET/GbxState.cs +++ b/Src/GBX.NET/GbxState.cs @@ -4,10 +4,12 @@ public class GbxState { private IList? idStrings; private SortedDictionary? auxNodes; + private SortedDictionary? extAuxNodes; public int? IdVersion { get; internal set; } public IList IdStrings => idStrings ??= new List(); public SortedDictionary AuxNodes => auxNodes ??= new(); + public SortedDictionary ExtAuxNodes => extAuxNodes ??= new(); public bool Encapsulated { get; } From 0b473c7e452eb6b2ede94b68de086a087d842689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Wed, 21 Dec 2022 11:46:28 +0100 Subject: [PATCH 09/45] Update SectionSocials.razor --- Tools/GbxExplorer/Client/Sections/SectionSocials.razor | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Tools/GbxExplorer/Client/Sections/SectionSocials.razor b/Tools/GbxExplorer/Client/Sections/SectionSocials.razor index df8a298d1..03a0bce24 100644 --- a/Tools/GbxExplorer/Client/Sections/SectionSocials.razor +++ b/Tools/GbxExplorer/Client/Sections/SectionSocials.razor @@ -20,10 +20,15 @@ @code { - private string branch = "master"; + private string? branch; protected override void OnInitialized() { branch = Config["Branch"]; + + if (string.IsNullOrWhiteSpace(branch)) + { + branch = "master"; + } } -} \ No newline at end of file +} From 89fd0c91ac6b5ac8181ff229b0748a18464645ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sat, 24 Dec 2022 02:14:18 +0100 Subject: [PATCH 10/45] Add Size to CGameCtnDecorationSize --- Src/GBX.NET/Engines/Game/CGameCtnDecorationSize.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnDecorationSize.cs b/Src/GBX.NET/Engines/Game/CGameCtnDecorationSize.cs index 72ac501fc..f8ebd10d5 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnDecorationSize.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnDecorationSize.cs @@ -6,6 +6,7 @@ public class CGameCtnDecorationSize : CMwNod private Vec2 editionZoneMin; private Vec2 editionZoneMax; private int baseHeightBase; + private Int3 size; private CSceneLayout? scene; private GameBoxRefTable.File? sceneFile; @@ -21,6 +22,10 @@ public class CGameCtnDecorationSize : CMwNod [AppliedWithChunk] public int BaseHeightBase { get => baseHeightBase; set => baseHeightBase = value; } + [NodeMember] + [AppliedWithChunk] + public Int3 Size { get => size; set => size = value; } + [NodeMember(ExactlyNamed = true)] [AppliedWithChunk] public CSceneLayout? Scene @@ -62,16 +67,10 @@ public override void ReadWrite(CGameCtnDecorationSize n, GameBoxReaderWriter rw) [Chunk(0x0303B001)] public class Chunk0303B001 : Chunk { - public int U01; - public int U02; - public int U03; - public override void ReadWrite(CGameCtnDecorationSize n, GameBoxReaderWriter rw) { rw.Int32(ref n.baseHeightBase); - rw.Int32(ref U01); - rw.Int32(ref U02); - rw.Int32(ref U03); + rw.Int3(ref n.size); rw.NodeRef(ref n.scene, ref n.sceneFile); } } From 949f26a9727b481806fd4557308c26702e67153b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sat, 24 Dec 2022 03:13:52 +0100 Subject: [PATCH 11/45] Add CControlEffectSimi 0x001 --- .../Engines/Control/CControlEffectSimi.Key.cs | 24 +++++++++++++------ .../Engines/Control/CControlEffectSimi.cs | 17 +++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/Src/GBX.NET/Engines/Control/CControlEffectSimi.Key.cs b/Src/GBX.NET/Engines/Control/CControlEffectSimi.Key.cs index 08e3f1b65..8de9677e2 100644 --- a/Src/GBX.NET/Engines/Control/CControlEffectSimi.Key.cs +++ b/Src/GBX.NET/Engines/Control/CControlEffectSimi.Key.cs @@ -41,18 +41,28 @@ public override void ReadWrite(GameBoxReaderWriter rw, int version) { base.ReadWrite(rw, version); + // GmSimi2::Archive rw.Vec2(ref position); rw.Single(ref rotation); rw.Vec2(ref scale); - rw.Single(ref opacity); - rw.Single(ref depth); + // - if (version != 2) + if (version >= 1) { - rw.Single(ref U01); - rw.Single(ref isContinuousEffect); - rw.Single(ref U02); - rw.Single(ref U03); + rw.Single(ref opacity); + + if (version >= 2) + { + rw.Single(ref depth); + + if (version >= 3) + { + rw.Single(ref U01); + rw.Single(ref isContinuousEffect); + rw.Single(ref U02); + rw.Single(ref U03); + } + } } } } diff --git a/Src/GBX.NET/Engines/Control/CControlEffectSimi.cs b/Src/GBX.NET/Engines/Control/CControlEffectSimi.cs index e5b4ca274..93578ea4b 100644 --- a/Src/GBX.NET/Engines/Control/CControlEffectSimi.cs +++ b/Src/GBX.NET/Engines/Control/CControlEffectSimi.cs @@ -89,6 +89,23 @@ public static CControlEffectSimiBuilder Create() #region Chunks + #region 0x001 chunk + + /// + /// CControlEffectSimi 0x001 chunk + /// + [Chunk(0x07010001)] + public class Chunk07010001 : Chunk + { + public override void ReadWrite(CControlEffectSimi n, GameBoxReaderWriter rw) + { + rw.List(ref n.keys!, (rw, x) => x.ReadWrite(rw, version: 1)); + rw.Boolean(ref n.centered); + } + } + + #endregion + #region 0x002 chunk /// From f6077c49a55bb0b0b108b330933e6912cc45f063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sat, 24 Dec 2022 03:19:58 +0100 Subject: [PATCH 12/45] Improve naming --- Src/GBX.NET/Node.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Src/GBX.NET/Node.cs b/Src/GBX.NET/Node.cs index f4d5d3961..f04024a96 100644 --- a/Src/GBX.NET/Node.cs +++ b/Src/GBX.NET/Node.cs @@ -483,7 +483,7 @@ private static async Task ReadUnskippableChunkAsync( } else { - await WriteChunkWithReadWriteAsync(node, (IReadableWritableChunk)chunk, gbxrw, cancellationToken); + await ReadWriteChunkAsync(node, (IReadableWritableChunk)chunk, gbxrw, cancellationToken); } } catch (EndOfStreamException) // May not be needed @@ -802,13 +802,13 @@ private async Task WriteUnskippableChunkAsync(IReadableWritableChunk chunk, return; } - await WriteChunkWithReadWriteAsync(chunk, rw, cancellationToken); + await ReadWriteChunkAsync(chunk, rw, cancellationToken); } - private static async Task WriteChunkWithReadWriteAsync(Node node, - IReadableWritableChunk chunk, - GameBoxReaderWriter rw, - CancellationToken cancellationToken) + private static async Task ReadWriteChunkAsync(Node node, + IReadableWritableChunk chunk, + GameBoxReaderWriter rw, + CancellationToken cancellationToken) { if (NodeManager.IsAsyncChunk(chunk.Id)) { @@ -824,11 +824,11 @@ private static async Task WriteChunkWithReadWriteAsync(Node node, chunk.ReadWrite(node, rw); } - private async Task WriteChunkWithReadWriteAsync(IReadableWritableChunk chunk, - GameBoxReaderWriter rw, - CancellationToken cancellationToken) + private async Task ReadWriteChunkAsync(IReadableWritableChunk chunk, + GameBoxReaderWriter rw, + CancellationToken cancellationToken) { - await WriteChunkWithReadWriteAsync(this, chunk, rw, cancellationToken); + await ReadWriteChunkAsync(this, chunk, rw, cancellationToken); } private void WriteSkippableChunk(ISkippableChunk skippableChunk, @@ -894,7 +894,7 @@ private async Task WriteSkippableChunkBodyAsync(ISkippableChunk skippableChunk, try { - await WriteChunkWithReadWriteAsync(skippableChunk, rw, cancellationToken); + await ReadWriteChunkAsync(skippableChunk, rw, cancellationToken); } catch (ChunkWriteNotImplementedException) { From 10ebeb340e8a68d6556b76f7f72df9ee2d1349f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sat, 24 Dec 2022 03:40:40 +0100 Subject: [PATCH 13/45] Add note about .NET Core 3.1 Runtime --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 39af60a4b..724c23549 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Here are some of the useful classes/types to start with: - Current C# language version is **11**. To build the solution: -- Installing Visual Studio 2022 with default .NET tools, **.NET WebAssembly Build Tools**, and **.NET Framework 4.6.2 Targeting Pack** is the easiest option. +- Installing Visual Studio 2022 with default .NET tools, **.NET WebAssembly Build Tools**, **.NET Core 3.1 Runtime**, and **.NET Framework 4.6.2 Targeting Pack** is the easiest option. - JetBrains Rider also works as it should. Visual Studio Code may work with a bit more setup. - Make sure you have all the needed targetting packs installed (currently **.NET 7.0**, .NET 6.0, .NET Standard 2.1, .NET Standard 2.0, and .NET Framework 4.6.2). From 12dc6681c9808ae9276c02d75519e8b18801ab35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sun, 25 Dec 2022 16:49:55 +0100 Subject: [PATCH 14/45] Cleanup CGameCtnCollector --- .../GameData/CGameCtnCollector.IHeader.cs | 2 +- .../Engines/GameData/CGameCtnCollector.cs | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.IHeader.cs b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.IHeader.cs index 121b66281..3baf9f355 100644 --- a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.IHeader.cs +++ b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.IHeader.cs @@ -14,6 +14,6 @@ public interface IHeader : INodeHeader public EProdState? ProdState { get; set; } public Color[,]? Icon { get; set; } public byte[]? IconWebP { get; set; } - public long FileTime { get; set; } + public ulong FileTime { get; set; } } } diff --git a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs index 112bdacc7..78aa2cf37 100644 --- a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs +++ b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs @@ -44,7 +44,7 @@ public enum EProdState private string? skinDirectory; private bool isInternal; private bool isAdvanced; - private long fileTime; + private ulong fileTime; #endregion @@ -100,7 +100,7 @@ public enum EProdState [NodeMember] [AppliedWithChunk] - public long FileTime { get => fileTime; set => fileTime = value; } + public ulong FileTime { get => fileTime; set => fileTime = value; } [NodeMember(ExactlyNamed = true)] [AppliedWithChunk] @@ -173,8 +173,8 @@ public class Chunk2E001003 : HeaderChunk, IVersionable public int Version { get => version; set => version = value; } - public int U01; - public int U02; + public string U01 = ""; + public string? U02; public int U03; public byte U04; public int U05; @@ -183,17 +183,17 @@ public class Chunk2E001003 : HeaderChunk, IVersionable public override void ReadWrite(CGameCtnCollector n, GameBoxReaderWriter rw) { rw.Ident(ref n.author!); - rw.Int32(ref version); + rw.Int32(ref version); // Id but filtered, technically rw.String(ref n.pageName!); if (version == 5) { - rw.Int32(ref U01); + rw.Id(ref U01!); } if (version >= 4) { - rw.Int32(ref U02); // Id? + rw.Id(ref U02); } if (version >= 3) @@ -314,7 +314,7 @@ public class Chunk2E001006H : HeaderChunk { public override void ReadWrite(CGameCtnCollector n, GameBoxReaderWriter rw) { - rw.Int64(ref n.fileTime); + rw.UInt64(ref n.fileTime); // SHeaderLightMap } } @@ -439,7 +439,7 @@ public class Chunk2E00100B : Chunk { public override void ReadWrite(CGameCtnCollector n, GameBoxReaderWriter rw) { - rw.Ident(ref n.author!); + rw.Ident(ref n.author!); // It may be called Ident } } @@ -505,7 +505,7 @@ public class Chunk2E001010 : Chunk, IVersionable private int version; public CMwNod? U01; - public int U02; + public CMwNod? U02; public int Version { get => version; set => version = value; } @@ -515,9 +515,9 @@ public override void ReadWrite(CGameCtnCollector n, GameBoxReaderWriter rw) rw.NodeRef(ref U01); rw.String(ref n.skinDirectory); - if (version >= 2 && n.skinDirectory!.Length == 0) + if (version >= 2 && (n.skinDirectory is null || n.skinDirectory.Length == 0)) { - rw.Int32(ref U02); // -1 + rw.NodeRef(ref U02); } } } From 9126fb56a9b9a7e0b1b109d8f1dd67bd1a643a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sun, 25 Dec 2022 16:51:34 +0100 Subject: [PATCH 15/45] Change Author to Ident --- Samples/BlocksAndTheirClipsTM2020/Program.cs | 2 +- Src/GBX.NET/Engines/Game/CGameCtnChallenge.cs | 6 +++--- .../Engines/GameData/CGameCtnCollector.IHeader.cs | 2 +- Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs | 12 ++++++------ .../Unit/Engines/GameData/CGameCtnCollectorTests.cs | 8 ++++---- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Samples/BlocksAndTheirClipsTM2020/Program.cs b/Samples/BlocksAndTheirClipsTM2020/Program.cs index 6a5346075..5bbd9cc25 100644 --- a/Samples/BlocksAndTheirClipsTM2020/Program.cs +++ b/Samples/BlocksAndTheirClipsTM2020/Program.cs @@ -32,7 +32,7 @@ Ground = CreateVariant(blockInfo.VariantBaseGround) }; - dict.Add(blockInfo.Author.Id, block); + dict.Add(blockInfo.Ident.Id, block); } catch (NotAGbxException) { diff --git a/Src/GBX.NET/Engines/Game/CGameCtnChallenge.cs b/Src/GBX.NET/Engines/Game/CGameCtnChallenge.cs index c50d62216..618d82dad 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnChallenge.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnChallenge.cs @@ -4470,14 +4470,14 @@ public override void Write(CGameCtnChallenge n, GameBoxWriter w) { var gbxItem = item.GetGbx() ?? throw new ThisShouldNotHappenException(); - if (item.Author is null) + if (item.Ident is null) { continue; } if (gbxItem.FileName is null) { - embedded.Add(item.Author); + embedded.Add(item.Ident); continue; } @@ -4494,7 +4494,7 @@ public override void Write(CGameCtnChallenge n, GameBoxWriter w) } } - embedded.Add(item.Author with { Id = id }); + embedded.Add(item.Ident with { Id = id }); } writer.WriteList(embedded, (x, w) => w.Write(x)); diff --git a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.IHeader.cs b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.IHeader.cs index 3baf9f355..58fa8fc1c 100644 --- a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.IHeader.cs +++ b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.IHeader.cs @@ -6,7 +6,7 @@ public partial class CGameCtnCollector { public interface IHeader : INodeHeader { - public Ident Author { get; set; } + public Ident Ident { get; set; } public string PageName { get; set; } public ECollectorFlags Flags { get; set; } public int CatalogPosition { get; set; } diff --git a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs index 78aa2cf37..cccf6324a 100644 --- a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs +++ b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs @@ -32,7 +32,7 @@ public enum EProdState #region Fields - private Ident author; + private Ident ident; private string pageName; private ECollectorFlags flags; private int catalogPosition; @@ -56,7 +56,7 @@ public enum EProdState [AppliedWithChunk] [AppliedWithChunk] [AppliedWithChunk] - public Ident Author { get => author; set => author = value; } + public Ident Ident { get => ident; set => ident = value; } [NodeMember(ExactlyNamed = true)] [AppliedWithChunk] @@ -137,7 +137,7 @@ public enum EProdState internal CGameCtnCollector() { - author = Ident.Empty; + ident = Ident.Empty; pageName = ""; } @@ -155,7 +155,7 @@ public class Chunk2E001002 : Chunk { public override void ReadWrite(CGameCtnCollector n, GameBoxReaderWriter rw) { - rw.Ident(ref n.author!); + rw.Ident(ref n.ident!); } } @@ -182,7 +182,7 @@ public class Chunk2E001003 : HeaderChunk, IVersionable public override void ReadWrite(CGameCtnCollector n, GameBoxReaderWriter rw) { - rw.Ident(ref n.author!); + rw.Ident(ref n.ident!); rw.Int32(ref version); // Id but filtered, technically rw.String(ref n.pageName!); @@ -439,7 +439,7 @@ public class Chunk2E00100B : Chunk { public override void ReadWrite(CGameCtnCollector n, GameBoxReaderWriter rw) { - rw.Ident(ref n.author!); // It may be called Ident + rw.Ident(ref n.ident!); // It may be called Ident } } diff --git a/Tests/GBX.NET.Tests/Unit/Engines/GameData/CGameCtnCollectorTests.cs b/Tests/GBX.NET.Tests/Unit/Engines/GameData/CGameCtnCollectorTests.cs index 9a255bd02..eb55098b9 100644 --- a/Tests/GBX.NET.Tests/Unit/Engines/GameData/CGameCtnCollectorTests.cs +++ b/Tests/GBX.NET.Tests/Unit/Engines/GameData/CGameCtnCollectorTests.cs @@ -98,7 +98,7 @@ public void ReadWrite_ManiaPlanet_Latest_ShouldRead() chunkTester.ReadWriteWithReader(); // Assert - Assert.Equal(expected: new("", "Stadium", "bigbang1112"), actual: chunkTester.Node.Author); + Assert.Equal(expected: new("", "Stadium", "bigbang1112"), actual: chunkTester.Node.Ident); } [Fact] @@ -113,7 +113,7 @@ public void ReadWrite_Trackmania2020_2022_7_6_ShouldRead() chunkTester.ReadWriteWithReader(); // Assert - Assert.Equal(expected: new("", new(26), "akPfIM0aSzuHuaaDWptBbQ"), actual: chunkTester.Node.Author); + Assert.Equal(expected: new("", new(26), "akPfIM0aSzuHuaaDWptBbQ"), actual: chunkTester.Node.Ident); } [Fact] @@ -124,7 +124,7 @@ public void ReadWrite_ManiaPlanet_Latest_ShouldWrite() = new(GameVersions.ManiaPlanet_Latest); chunkTester.SetIdState(version: 3); - chunkTester.Node.Author = new("", "Stadium", "bigbang1112"); + chunkTester.Node.Ident = new("", "Stadium", "bigbang1112"); // Act chunkTester.ReadWriteWithWriter(); @@ -141,7 +141,7 @@ public void ReadWrite_Trackmania2020_2022_7_6_ShouldWrite() = new(GameVersions.Trackmania2020_2022_7_6); chunkTester.SetIdState(version: 3); - chunkTester.Node.Author = new("", new(26), "akPfIM0aSzuHuaaDWptBbQ"); + chunkTester.Node.Ident = new("", new(26), "akPfIM0aSzuHuaaDWptBbQ"); // Act chunkTester.ReadWriteWithWriter(); From 008f03357e437a05b2f1db6db0fbfcddaa7e802c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sun, 25 Dec 2022 16:52:49 +0100 Subject: [PATCH 16/45] Update to 1.1.1 --- Src/GBX.NET/GBX.NET.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/GBX.NET/GBX.NET.csproj b/Src/GBX.NET/GBX.NET.csproj index 3f559ca53..f78b6f381 100644 --- a/Src/GBX.NET/GBX.NET.csproj +++ b/Src/GBX.NET/GBX.NET.csproj @@ -11,7 +11,7 @@ enable enable - 1.1.0 + 1.1.1 net7.0;net6.0;netstandard2.1;netstandard2.0;net462 From c6afb454d67fa6030bc8a6877859eacc005d7346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sun, 25 Dec 2022 17:02:47 +0100 Subject: [PATCH 17/45] Remove build-docs --- .../workflows/deploy-gbxexplorer-pages.yml | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/.github/workflows/deploy-gbxexplorer-pages.yml b/.github/workflows/deploy-gbxexplorer-pages.yml index 4fba3b4f5..2eb1f456e 100644 --- a/.github/workflows/deploy-gbxexplorer-pages.yml +++ b/.github/workflows/deploy-gbxexplorer-pages.yml @@ -66,51 +66,9 @@ jobs: name: app path: publish/wwwroot retention-days: 1 - - build-docs: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Clone wiki - run: | - git clone https://github.com/BigBang1112/gbx-net.wiki.git wiki - cp -r wiki/* Docs/wiki - - - name: Create global.json - uses: 1arp/create-a-file-action@0.2 - with: - path: Docs - file: global.json - content: | - { - "_gitContribute": { - "repo": "https://github.com/BigBang1112/gbx-net", - "branch": "${{ github.ref_name }}" - } - } - - - name: Generate docs - uses: nikeee/docfx-action@v1.0.0 - with: - args: Docs/docfx.json --globalMetadataFiles global.json - - - name: Copy docs to root - run: cp -r Docs/_site docs - - - name: Upload docs artifact - uses: actions/upload-artifact@v3.1.0 - with: - name: docs - path: docs - retention-days: 1 deploy: - needs: [build-gbxexplorer, build-docs] + needs: build-gbxexplorer environment: name: github-pages From 36b744876edc941a069922b964ef4bcfb585b887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sun, 25 Dec 2022 17:11:24 +0100 Subject: [PATCH 18/45] Add Triangles3D.Create --- ...ameCtnMediaBlockTriangles3DBuilder.TMUF.cs | 23 +++++++++++++ .../CGameCtnMediaBlockTriangles3DBuilder.cs | 34 +++++++++++++++++++ .../Game/CGameCtnMediaBlockTriangles.Key.cs | 2 +- .../Game/CGameCtnMediaBlockTriangles.cs | 10 +++--- .../Game/CGameCtnMediaBlockTriangles3D.cs | 7 +++- 5 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 Src/GBX.NET/Builders/Engines/Game/CGameCtnMediaBlockTriangles3DBuilder.TMUF.cs create mode 100644 Src/GBX.NET/Builders/Engines/Game/CGameCtnMediaBlockTriangles3DBuilder.cs diff --git a/Src/GBX.NET/Builders/Engines/Game/CGameCtnMediaBlockTriangles3DBuilder.TMUF.cs b/Src/GBX.NET/Builders/Engines/Game/CGameCtnMediaBlockTriangles3DBuilder.TMUF.cs new file mode 100644 index 000000000..e59288bfc --- /dev/null +++ b/Src/GBX.NET/Builders/Engines/Game/CGameCtnMediaBlockTriangles3DBuilder.TMUF.cs @@ -0,0 +1,23 @@ +namespace GBX.NET.Builders.Engines.Game; + +public partial class CGameCtnMediaBlockTriangles3DBuilder +{ + public class TMUF : GameBuilder + { + public IList? Keys { get; set; } + + public TMUF(CGameCtnMediaBlockTriangles3DBuilder baseBuilder, CGameCtnMediaBlockTriangles3D node) : base(baseBuilder, node) { } + + public TMUF WithKeys(Func> keys) + { + Keys = keys(Node); + return this; + } + + public override CGameCtnMediaBlockTriangles3D Build() + { + Node.Keys = Keys ?? new List(); + return Node; + } + } +} \ No newline at end of file diff --git a/Src/GBX.NET/Builders/Engines/Game/CGameCtnMediaBlockTriangles3DBuilder.cs b/Src/GBX.NET/Builders/Engines/Game/CGameCtnMediaBlockTriangles3DBuilder.cs new file mode 100644 index 000000000..2974addeb --- /dev/null +++ b/Src/GBX.NET/Builders/Engines/Game/CGameCtnMediaBlockTriangles3DBuilder.cs @@ -0,0 +1,34 @@ +namespace GBX.NET.Builders.Engines.Game; + +public partial class CGameCtnMediaBlockTriangles3DBuilder : Builder +{ + public Vec4[] Vertices { get; } + public Int3[]? Triangles { get; set; } + + /// Array of vertex colors. + public CGameCtnMediaBlockTriangles3DBuilder(Vec4[] vertices) + { + Vertices = vertices; + } + + public CGameCtnMediaBlockTriangles3DBuilder WithTriangles(Int3[] triangles) + { + Triangles = triangles; + return this; + } + + public TMUF ForTMUF() => new(this, NewNode()); + + internal CGameCtnMediaBlockTriangles3D NewNode() + { + var node = new CGameCtnMediaBlockTriangles3D + { + Vertices = Vertices, + Triangles = Triangles ?? Array.Empty() + }; + + node.CreateChunk(); + + return node; + } +} \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.Key.cs b/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.Key.cs index 61d827df8..3cb6f4308 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.Key.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.Key.cs @@ -31,7 +31,7 @@ public Vec3[] Positions public Key(CGameCtnMediaBlockTriangles node) { this.node = node; - positions = new Vec3[node.vertices?.Length ?? 0]; + positions = new Vec3[node.vertices.Length]; } } } diff --git a/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.cs b/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.cs index 1d1f851b4..2b22cd019 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.cs @@ -10,9 +10,9 @@ public abstract partial class CGameCtnMediaBlockTriangles : CGameCtnMediaBlock, { #region Fields - private IList keys; - private Vec4[] vertices; - private Int3[] triangles; + private IList keys = Array.Empty(); + private Vec4[] vertices = Array.Empty(); + private Int3[] triangles = Array.Empty(); #endregion @@ -83,9 +83,7 @@ public Int3[] Triangles internal CGameCtnMediaBlockTriangles() { - keys = Array.Empty(); - vertices = Array.Empty(); - triangles = Array.Empty(); + } #endregion diff --git a/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles3D.cs b/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles3D.cs index ce6b51b6e..2e61eba52 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles3D.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles3D.cs @@ -1,4 +1,6 @@ -namespace GBX.NET.Engines.Game; +using GBX.NET.Builders.Engines.Game; + +namespace GBX.NET.Engines.Game; /// /// MediaTracker block - 3D triangles. @@ -12,4 +14,7 @@ internal CGameCtnMediaBlockTriangles3D() { } + + /// Array of vertex colors. + public static CGameCtnMediaBlockTriangles3DBuilder Create(Vec4[] vertices) => new(vertices); } From 355afb73c8123279c27d55f9e8916faa451b15a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sun, 25 Dec 2022 17:14:28 +0100 Subject: [PATCH 19/45] Remove "Download docs artifact" --- .github/workflows/deploy-gbxexplorer-pages.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/deploy-gbxexplorer-pages.yml b/.github/workflows/deploy-gbxexplorer-pages.yml index 2eb1f456e..b84f05104 100644 --- a/.github/workflows/deploy-gbxexplorer-pages.yml +++ b/.github/workflows/deploy-gbxexplorer-pages.yml @@ -81,12 +81,6 @@ jobs: uses: actions/download-artifact@v3.0.0 with: name: app - - - name: Download docs artifact - uses: actions/download-artifact@v3.0.0 - with: - name: docs - path: docs - name: Setup Pages uses: actions/configure-pages@v2 From 95bb903ad84f2c040dcff902f8a5652e888fc237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sun, 25 Dec 2022 23:55:02 +0100 Subject: [PATCH 20/45] Fix Mat4.Zero --- Src/GBX.NET/Mat4.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/GBX.NET/Mat4.cs b/Src/GBX.NET/Mat4.cs index 2a5ede90a..004bbbe95 100644 --- a/Src/GBX.NET/Mat4.cs +++ b/Src/GBX.NET/Mat4.cs @@ -5,5 +5,5 @@ public readonly record struct Mat4(float XX, float XY, float XZ, float XW, float ZX, float ZY, float ZZ, float ZW, float WX, float WY, float WZ, float WW) { - public static readonly Iso4 Zero = new(); + public static readonly Mat4 Zero = new(); } From e774283f3d0358199e4960ae01a156c6f523d033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sun, 25 Dec 2022 23:55:30 +0100 Subject: [PATCH 21/45] Add Mat3 --- Src/GBX.NET/GameBoxReader.cs | 13 ++++++++ Src/GBX.NET/GameBoxReaderWriter.cs | 50 ++++++++++++++++++++++++++++++ Src/GBX.NET/GameBoxWriter.cs | 15 +++++++++ Src/GBX.NET/Mat3.cs | 8 +++++ 4 files changed, 86 insertions(+) create mode 100644 Src/GBX.NET/Mat3.cs diff --git a/Src/GBX.NET/GameBoxReader.cs b/Src/GBX.NET/GameBoxReader.cs index 3e847bedf..953818b72 100644 --- a/Src/GBX.NET/GameBoxReader.cs +++ b/Src/GBX.NET/GameBoxReader.cs @@ -422,6 +422,19 @@ public Iso4 ReadIso4() TZ: ReadSingle()); } + public Mat3 ReadMat3() + { + return new Mat3(XX: ReadSingle(), + XY: ReadSingle(), + XZ: ReadSingle(), + YX: ReadSingle(), + YY: ReadSingle(), + YZ: ReadSingle(), + ZX: ReadSingle(), + ZY: ReadSingle(), + ZZ: ReadSingle()); + } + public Mat4 ReadMat4() { return new Mat4(XX: ReadSingle(), diff --git a/Src/GBX.NET/GameBoxReaderWriter.cs b/Src/GBX.NET/GameBoxReaderWriter.cs index a82d693d0..1092e0d29 100644 --- a/Src/GBX.NET/GameBoxReaderWriter.cs +++ b/Src/GBX.NET/GameBoxReaderWriter.cs @@ -1185,6 +1185,56 @@ public Iso4 Iso4(Iso4 variable = default) /// An I/O error occurs. public void Iso4(ref Iso4? variable, Iso4 defaultValue = default) => variable = Iso4(variable, defaultValue); + /// + /// Reads or writes an . + /// + /// Variable to write. Ignored in read mode. + /// Value read in read mode. In write mode, is returned. + /// The end of the stream is reached. + /// The stream is closed. + /// An I/O error occurs. + public Mat3 Mat3(Mat3 variable = default) + { + if (Reader is not null) variable = Reader.ReadMat3(); + if (Writer is not null) Writer.Write(variable); + return variable; + } + + /// + /// Reads or writes a nullable . + /// + /// Variable to write. If null, is written. Ignored in read mode. + /// Value written when is null. Ignored in read mode. + /// Value read in read mode. In write mode, is returned (including null). + /// The end of the stream is reached. + /// The stream is closed. + /// An I/O error occurs. + public Mat3? Mat3(Mat3? variable, Mat3 defaultValue = default) + { + if (Reader is not null) variable = Reader.ReadMat3(); + if (Writer is not null) Writer.Write(variable.GetValueOrDefault(defaultValue)); + return variable; + } + + /// + /// Reads or writes an through reference. + /// + /// Variable to read or write. Read mode sets , write mode uses to write the value (keeping unchanged). + /// The end of the stream is reached. + /// The stream is closed. + /// An I/O error occurs. + public void Mat3(ref Mat3 variable) => variable = Mat3(variable); + + /// + /// Reads or writes a nullable through reference. + /// + /// Variable to read or write. Read mode sets , write mode uses to write the value (keeping unchanged). If is null, is written instead. + /// Value written when is null. Ignored in read mode. + /// The end of the stream is reached. + /// The stream is closed. + /// An I/O error occurs. + public void Mat3(ref Mat3? variable, Mat3 defaultValue = default) => variable = Mat3(variable, defaultValue); + /// /// Reads or writes an . /// diff --git a/Src/GBX.NET/GameBoxWriter.cs b/Src/GBX.NET/GameBoxWriter.cs index 8a0e81e5e..876b46dd5 100644 --- a/Src/GBX.NET/GameBoxWriter.cs +++ b/Src/GBX.NET/GameBoxWriter.cs @@ -198,6 +198,21 @@ public void Write(Iso4 value) Write(value.TZ); } + /// An I/O error occurs. + /// The stream is closed. + public void Write(Mat3 value) + { + Write(value.XX); + Write(value.XY); + Write(value.XZ); + Write(value.YX); + Write(value.YY); + Write(value.YZ); + Write(value.ZX); + Write(value.ZY); + Write(value.ZZ); + } + /// An I/O error occurs. /// The stream is closed. public void Write(Mat4 value) diff --git a/Src/GBX.NET/Mat3.cs b/Src/GBX.NET/Mat3.cs new file mode 100644 index 000000000..b8ff170bb --- /dev/null +++ b/Src/GBX.NET/Mat3.cs @@ -0,0 +1,8 @@ +namespace GBX.NET; + +public readonly record struct Mat3(float XX, float XY, float XZ, + float YX, float YY, float YZ, + float ZX, float ZY, float ZZ) +{ + public static readonly Mat3 Zero = new(); +} From 4ded4f52b84f05860d62c8e29a99c7a28bdd95b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Sun, 25 Dec 2022 23:56:01 +0100 Subject: [PATCH 22/45] Improve CPlugSolid --- Src/GBX.NET/Engines/Plug/CPlugSolid.cs | 132 +++++++++++-------------- 1 file changed, 56 insertions(+), 76 deletions(-) diff --git a/Src/GBX.NET/Engines/Plug/CPlugSolid.cs b/Src/GBX.NET/Engines/Plug/CPlugSolid.cs index 92e9d8906..877afdcea 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugSolid.cs +++ b/Src/GBX.NET/Engines/Plug/CPlugSolid.cs @@ -11,10 +11,16 @@ namespace GBX.NET.Engines.Plug; [NodeExtension("Solid")] public class CPlugSolid : CPlug { + private int typeAndIndex; private CPlug? tree; private GameBoxRefTable.File? treeFile; + private PreLightGen? solidPreLightGen; + private ulong fileWriteTime; - [NodeMember] + [NodeMember(ExactlyNamed = true)] + public int TypeAndIndex { get => typeAndIndex; set => typeAndIndex = value; } + + [NodeMember(ExactlyNamed = true)] [AppliedWithChunk] [AppliedWithChunk] public CPlug? Tree @@ -23,6 +29,14 @@ public CPlug? Tree set => tree = value; } + [NodeMember(ExactlyNamed = true)] + [AppliedWithChunk] + public PreLightGen? SolidPreLightGen { get => solidPreLightGen; set => solidPreLightGen = value; } + + [NodeMember(ExactlyNamed = true)] + [AppliedWithChunk] + public ulong FileWriteTime { get => fileWriteTime; set => fileWriteTime = value; } + internal CPlugSolid() { @@ -86,11 +100,9 @@ public void ExportToObj(Stream objStream, [Chunk(0x09005000)] public class Chunk09005000 : Chunk { - public int U01; - public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) { - rw.Int32(ref U01); + rw.Int32(ref n.typeAndIndex); } } @@ -107,15 +119,7 @@ public class Chunk09005006 : Chunk public float U05; public float U06; - public float U07; - public float U08; - public float U09; - public float U10; - public float U11; - public float U12; - public float U13; - public float U14; - public float U15; + public Mat3 U07; public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) { @@ -126,15 +130,7 @@ public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) rw.Single(ref U05); rw.Single(ref U06); - rw.Single(ref U07); - rw.Single(ref U08); - rw.Single(ref U09); - rw.Single(ref U10); - rw.Single(ref U11); - rw.Single(ref U12); - rw.Single(ref U13); - rw.Single(ref U14); - rw.Single(ref U15); + rw.Mat3(ref U07); } } @@ -184,45 +180,30 @@ public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) [Chunk(0x0900500C)] public class Chunk0900500C : Chunk { - public bool U01; - public bool U02; - public bool U03; - public bool U04; - public bool U05; - public bool U06; - public bool U07; - public bool U08; - public bool U09; - public bool U10; - public float U11; - public int U12; - public float U13; - public float U14; - public float U15; - public float U16; - public int U17; - public int U18; + public bool U01; // always false + public float U02; // always zero + public int U03; // always zero public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) { rw.Boolean(ref U01); - rw.Boolean(ref U02); - rw.Boolean(ref U03); - rw.Boolean(ref U04); - rw.Boolean(ref U05); - rw.Boolean(ref U06); - rw.Boolean(ref U07); - rw.Boolean(ref U08); - rw.Boolean(ref U09); - rw.Boolean(ref U10); - rw.Single(ref U11); - rw.Int32(ref U12); - rw.Single(ref U13); - rw.Single(ref U14); - rw.Single(ref U15); - rw.Single(ref U16); - rw.Int32(ref U17); - rw.Int32(ref U18); + rw.Boolean(ref U01); + rw.Boolean(ref U01); + rw.Boolean(ref U01); + rw.Boolean(ref U01); + rw.Boolean(ref U01); + rw.Boolean(ref U01); + rw.Boolean(ref U01); + rw.Boolean(ref U01); + rw.Boolean(ref U01); + rw.Single(ref U02); + rw.Int32(ref U03); + rw.Single(ref U02); + rw.Single(ref U02); + rw.Single(ref U02); + rw.Single(ref U02); + rw.Int32(ref U03); + rw.Int32(ref U03); } } @@ -264,7 +245,7 @@ public class Chunk0900500E : Chunk public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) { - rw.Single(ref U01); + rw.Single(ref U01); // probably related to CPlugPhysicalObject::SetComPos rw.Single(ref U02); rw.Single(ref U03); rw.Single(ref U04); @@ -299,7 +280,7 @@ public class Chunk09005010 : Chunk public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) { - rw.NodeRef(ref U01); + rw.NodeRef(ref U01); // CSceneVehicleEnvironment } } @@ -323,7 +304,7 @@ public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) rw.Boolean(ref U03); } - rw.NodeRef(ref n.tree, ref n.treeFile); + rw.NodeRef(ref n.tree, ref n.treeFile); // only of U02 is false? } public override async Task ReadWriteAsync(CPlugSolid n, GameBoxReaderWriter rw, CancellationToken cancellationToken = default) @@ -336,7 +317,7 @@ public override async Task ReadWriteAsync(CPlugSolid n, GameBoxReaderWriter rw, rw.Boolean(ref U03); } - n.tree = await rw.NodeRefAsync(n.tree, cancellationToken); + n.tree = await rw.NodeRefAsync(n.tree, cancellationToken); // only of U02 is false? } } @@ -350,16 +331,16 @@ public class Chunk09005012 : Chunk public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) { - rw.Byte(ref U01); + rw.Byte(ref U01); // relates ti SolidPreLightGen } } - #region 0x017 chunk + #region 0x017 chunk (SolidPreLightGen) /// - /// CPlugSolid 0x017 chunk + /// CPlugSolid 0x017 chunk (SolidPreLightGen) /// - [Chunk(0x09005017)] + [Chunk(0x09005017, "SolidPreLightGen")] public class Chunk09005017 : Chunk, IVersionable { private int version = 3; @@ -371,7 +352,6 @@ public class Chunk09005017 : Chunk, IVersionable public Rect U05; public Int2 U06; public Box U07; - public ulong U08; public bool U09; public PreLightGen? U10; @@ -387,7 +367,7 @@ public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) if (U09) { - rw.Archive(ref U10); + rw.Archive(ref n.solidPreLightGen); } } else @@ -407,7 +387,7 @@ public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) if (version >= 2) { - rw.UInt64(ref U08); + rw.UInt64(ref n.fileWriteTime); } } } @@ -426,14 +406,14 @@ public class Chunk09005019 : Chunk private int listVersion1 = 10; private int listVersion2 = 10; - private CPlugSound?[]? U01; - private CPlugParticleEmitterModel?[]? U02; - private LocatedInstance[]? U03; - private LocatedInstance[]? U04; - private int U05; - private string[]? U06; - private Iso4[]? U07; - private string? U08; + public CPlugSound?[]? U01; + public CPlugParticleEmitterModel?[]? U02; + public LocatedInstance[]? U03; + public LocatedInstance[]? U04; + public int U05; + public string[]? U06; + public Iso4[]? U07; + public string? U08; public int Version { get => version; set => version = value; } From aaaf51cb7f5927de7896da9dcd869fda51f9d28b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Mon, 26 Dec 2022 00:13:03 +0100 Subject: [PATCH 23/45] Add FileTime --- Src/GBX.NET/Engines/Plug/CPlugSolid.cs | 6 ++-- Src/GBX.NET/GameBoxReader.cs | 5 +++ Src/GBX.NET/GameBoxReaderWriter.cs | 50 ++++++++++++++++++++++++++ Src/GBX.NET/GameBoxWriter.cs | 5 +++ 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/Src/GBX.NET/Engines/Plug/CPlugSolid.cs b/Src/GBX.NET/Engines/Plug/CPlugSolid.cs index 877afdcea..15b6c0efc 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugSolid.cs +++ b/Src/GBX.NET/Engines/Plug/CPlugSolid.cs @@ -15,7 +15,7 @@ public class CPlugSolid : CPlug private CPlug? tree; private GameBoxRefTable.File? treeFile; private PreLightGen? solidPreLightGen; - private ulong fileWriteTime; + private DateTime? fileWriteTime; [NodeMember(ExactlyNamed = true)] public int TypeAndIndex { get => typeAndIndex; set => typeAndIndex = value; } @@ -35,7 +35,7 @@ public CPlug? Tree [NodeMember(ExactlyNamed = true)] [AppliedWithChunk] - public ulong FileWriteTime { get => fileWriteTime; set => fileWriteTime = value; } + public DateTime? FileWriteTime { get => fileWriteTime; set => fileWriteTime = value; } internal CPlugSolid() { @@ -387,7 +387,7 @@ public override void ReadWrite(CPlugSolid n, GameBoxReaderWriter rw) if (version >= 2) { - rw.UInt64(ref n.fileWriteTime); + rw.FileTime(ref n.fileWriteTime); } } } diff --git a/Src/GBX.NET/GameBoxReader.cs b/Src/GBX.NET/GameBoxReader.cs index 953818b72..c61b12546 100644 --- a/Src/GBX.NET/GameBoxReader.cs +++ b/Src/GBX.NET/GameBoxReader.cs @@ -2093,4 +2093,9 @@ public ExternalNode[] ReadExternalNodeArray() where T : Node { return ReadExternalNodeArray(length: ReadInt32()); } + + public DateTime ReadFileTime() + { + return DateTime.FromFileTimeUtc(ReadInt64()); + } } diff --git a/Src/GBX.NET/GameBoxReaderWriter.cs b/Src/GBX.NET/GameBoxReaderWriter.cs index 1092e0d29..04a20f406 100644 --- a/Src/GBX.NET/GameBoxReaderWriter.cs +++ b/Src/GBX.NET/GameBoxReaderWriter.cs @@ -1312,6 +1312,56 @@ public void TimeOfDay(ref TimeSpan? variable) variable = TimeOfDay(variable); } + /// + /// Reads or writes an . + /// + /// Variable to write. Ignored in read mode. + /// Value read in read mode. In write mode, is returned. + /// The end of the stream is reached. + /// The stream is closed. + /// An I/O error occurs. + public DateTime FileTime(DateTime variable = default) + { + if (Reader is not null) variable = Reader.ReadFileTime(); + if (Writer is not null) Writer.Write(variable); + return variable; + } + + /// + /// Reads or writes a nullable . + /// + /// Variable to write. If null, is written. Ignored in read mode. + /// Value written when is null. Ignored in read mode. + /// Value read in read mode. In write mode, is returned (including null). + /// The end of the stream is reached. + /// The stream is closed. + /// An I/O error occurs. + public DateTime? FileTime(DateTime? variable, DateTime defaultValue = default) + { + if (Reader is not null) variable = Reader.ReadFileTime(); + if (Writer is not null) Writer.Write(variable.GetValueOrDefault(defaultValue)); + return variable; + } + + /// + /// Reads or writes an through reference. + /// + /// Variable to read or write. Read mode sets , write mode uses to write the value (keeping unchanged). + /// The end of the stream is reached. + /// The stream is closed. + /// An I/O error occurs. + public void FileTime(ref DateTime variable) => variable = FileTime(variable); + + /// + /// Reads or writes a nullable through reference. + /// + /// Variable to read or write. Read mode sets , write mode uses to write the value (keeping unchanged). If is null, is written instead. + /// Value written when is null. Ignored in read mode. + /// The end of the stream is reached. + /// The stream is closed. + /// An I/O error occurs. + public void FileTime(ref DateTime? variable, DateTime defaultValue = default) => variable = FileTime(variable, defaultValue); + /// /// Reads or writes a . /// diff --git a/Src/GBX.NET/GameBoxWriter.cs b/Src/GBX.NET/GameBoxWriter.cs index 876b46dd5..322a74355 100644 --- a/Src/GBX.NET/GameBoxWriter.cs +++ b/Src/GBX.NET/GameBoxWriter.cs @@ -984,4 +984,9 @@ public void WriteExternalNodeArray(ExternalNode[]? array) where T : Node { WriteArray(array, (x, w) => w.Write(x.Node, x.File)); } + + public void Write(DateTime variable) + { + Write(variable.ToFileTimeUtc()); + } } From 9fee944e45727f3be356945e5714873b52022c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Mon, 26 Dec 2022 02:53:26 +0100 Subject: [PATCH 24/45] Update ghost namings --- Src/GBX.NET/Engines/Game/CGameCtnGhost.cs | 2 +- .../Engines/Game/CGameCtnReplayRecord.cs | 16 +++- Src/GBX.NET/Engines/Game/CGameGhost.Data.cs | 85 ++++++++++--------- Src/GBX.NET/Engines/Game/CGameGhost.cs | 6 +- 4 files changed, 64 insertions(+), 45 deletions(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.cs index e9f341e89..039e0610c 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.cs @@ -913,7 +913,7 @@ public class Chunk03092014 : SkippableChunk public override void ReadWrite(CGameCtnGhost n, GameBoxReaderWriter rw) { - rw.Int32(ref U01); + rw.Int32(ref U01); // some validation version } } diff --git a/Src/GBX.NET/Engines/Game/CGameCtnReplayRecord.cs b/Src/GBX.NET/Engines/Game/CGameCtnReplayRecord.cs index d5c0503d3..ef3cdca72 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnReplayRecord.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnReplayRecord.cs @@ -754,8 +754,8 @@ public override void Read(CGameCtnReplayRecord n, GameBoxReader r) { _ = r.ReadInt32(); // listVersion n.ghosts = r.ReadArray(r => r.ReadNodeRef()!); - U01 = r.ReadInt32(); - n.extras = r.ReadArray(r => r.ReadInt64()); + U01 = r.ReadInt32(); // always zero + n.extras = r.ReadArray(r => r.ReadInt64()); // SOldShowTime } public override async Task ReadAsync(CGameCtnReplayRecord n, GameBoxReader r, CancellationToken cancellationToken = default) @@ -900,7 +900,17 @@ public class Chunk0309301E : SkippableChunk [Chunk(0x0309301F, "AnchoredObjectInfos"), IgnoreChunk] public class Chunk0309301F : SkippableChunk { - + /* + * version 1: SOldGameCtnReplayRecord_AnchoredObjectInfos array + * - bool + * - int + * - float + * - float + * - float + * - int + * + * version 2: noderef CGameReplayObjectVisData m_Scenery_Objects_Deprecated + */ } #endregion diff --git a/Src/GBX.NET/Engines/Game/CGameGhost.Data.cs b/Src/GBX.NET/Engines/Game/CGameGhost.Data.cs index 0b1e6a9e4..332984443 100644 --- a/Src/GBX.NET/Engines/Game/CGameGhost.Data.cs +++ b/Src/GBX.NET/Engines/Game/CGameGhost.Data.cs @@ -28,7 +28,7 @@ public TimeInt32 SamplePeriod public ObservableCollection Samples { get; private set; } public CompressionLevel Compression { get; set; } - public uint NodeID { get; set; } + public uint SavedMobilClassId { get; set; } public Data() { @@ -86,51 +86,60 @@ public void Read(Stream stream) /// Reader. public void Read(GameBoxReader r) { - NodeID = r.ReadUInt32(); // CSceneVehicleCar or CSceneMobilCharVis - - if (NodeID != uint.MaxValue) + SavedMobilClassId = r.ReadUInt32(); // CSceneVehicleCar or CSceneMobilCharVis + + if (SavedMobilClassId == uint.MaxValue) { - var bSkipList2 = r.ReadBoolean(); - var u01 = r.ReadInt32(); - SamplePeriod = TimeInt32.FromMilliseconds(r.ReadInt32()); - var u02 = r.ReadInt32(); - - var sampleData = r.ReadBytes(); - - int sizePerSample = -1; - int[]? sampleSizes = null; + return; + } - var numSamples = r.ReadInt32(); - if (numSamples > 0) + var bSkipList2 = r.ReadBoolean(); // IsFixedTimeStep + var u01 = r.ReadInt32(); + SamplePeriod = TimeInt32.FromMilliseconds(r.ReadInt32()); // SavedPeriod + var u02 = r.ReadInt32(); + + var sampleData = r.ReadBytes(); // CGameGhostTMData::ArchiveStateBuffer + + // CGameGhostTMData::ArchiveStateOffsets + var sizePerSample = -1; + var sampleSizes = default(int[]); + + var numSamples = r.ReadInt32(); // StateOffsets count + + if (numSamples > 0) + { + var firstSampleOffset = r.ReadInt32(); + + if (numSamples > 1) { - var firstSampleOffset = r.ReadInt32(); - if (numSamples > 1) + sizePerSample = r.ReadInt32(); + + if (sizePerSample == -1) { - sizePerSample = r.ReadInt32(); - if (sizePerSample == -1) - { - sampleSizes = r.ReadArray(numSamples - 1); - } + sampleSizes = r.ReadArray(numSamples - 1); } } + } + // - int[]? sampleTimes = null; + // CGameGhostTMData::ArchiveStateTimes + var sampleTimes = default(int[]); - if (!bSkipList2) - { - sampleTimes = r.ReadArray(); - } + if (!bSkipList2) + { + sampleTimes = r.ReadArray(); + } + // - if (numSamples > 0) - { - using var mssd = new MemoryStream(sampleData); - ReadSamples(mssd, numSamples, sizePerSample, sampleSizes, sampleTimes); - } - else - { - Samples = new ObservableCollection(); - Samples.CollectionChanged += Samples_CollectionChanged; - } + if (numSamples > 0) + { + using var mssd = new MemoryStream(sampleData); + ReadSamples(mssd, numSamples, sizePerSample, sampleSizes, sampleTimes); + } + else + { + Samples = new ObservableCollection(); + Samples.CollectionChanged += Samples_CollectionChanged; } } @@ -156,7 +165,7 @@ public void ReadSamples(MemoryStream ms, int numSamples, int sizePerSample, int[ var time = sampleTimes?[i]; - switch (NodeID) + switch (SavedMobilClassId) { case 0x0A02B000: // CSceneVehicleCar { diff --git a/Src/GBX.NET/Engines/Game/CGameGhost.cs b/Src/GBX.NET/Engines/Game/CGameGhost.cs index 7283ede9c..c85d94947 100644 --- a/Src/GBX.NET/Engines/Game/CGameGhost.cs +++ b/Src/GBX.NET/Engines/Game/CGameGhost.cs @@ -16,7 +16,7 @@ public partial class CGameGhost : CMwNod [AppliedWithChunk] public bool IsReplaying { get => isReplaying; set => isReplaying = value; } - [NodeMember] + [NodeMember(ExactName = "TM_Data")] [AppliedWithChunk] [AppliedWithChunk] [AppliedWithChunk] @@ -92,7 +92,7 @@ public override void ReadWrite(CGameGhost n, GameBoxReaderWriter rw) ghostData.ReadSamples(ms, numSamples: Samples?.Length ?? 0, sizePerSample: 56); - if (ghostData.NodeID == uint.MaxValue) + if (ghostData.SavedMobilClassId == uint.MaxValue) return null; return ghostData; @@ -149,7 +149,7 @@ public override void ReadWrite(CGameGhost n, GameBoxReaderWriter rw) using var ms = new MemoryStream(Data); ghostData.Read(ms, compressed: true); - if (ghostData.NodeID == uint.MaxValue) + if (ghostData.SavedMobilClassId == uint.MaxValue) return null; return ghostData; From f98e67903c4902f43871e4519e5ea12af45b1fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Wed, 28 Dec 2022 21:31:17 +0100 Subject: [PATCH 25/45] Add Shootmania inputs, break TM2020 inputs --- Src/GBX.NET/BitReader.cs | 67 +++++ .../Game/CGameCtnGhost.PlayerInputData.cs | 231 ++++++++++-------- 2 files changed, 194 insertions(+), 104 deletions(-) create mode 100644 Src/GBX.NET/BitReader.cs diff --git a/Src/GBX.NET/BitReader.cs b/Src/GBX.NET/BitReader.cs new file mode 100644 index 000000000..afbf2a29a --- /dev/null +++ b/Src/GBX.NET/BitReader.cs @@ -0,0 +1,67 @@ +namespace GBX.NET; + +public class BitReader +{ + private readonly byte[] data; + + public int Position { get; private set; } + public int Length { get; } + + public BitReader(byte[] data) + { + this.data = data; + + Length = data.Length * 8; + } + + public bool ReadBit() + { + var result = (data[Position / 8] & (1 << (Position % 8))) != 0; + Position++; + return result; + } + + public int ReadNumber(int bits) + { + var result = 0; + for (var i = 0; i < bits; i++) + result |= (ReadBit() ? 1 : 0) << i; + return result; + } + + public byte Read2Bit() + { + return (byte)ReadNumber(bits: 2); + } + + public byte ReadByte() + { + return (byte)ReadNumber(bits: 8); + } + + public sbyte ReadSByte() + { + return (sbyte)ReadNumber(bits: 8); + } + + public short ReadInt16() + { + return (short)ReadNumber(bits: 16); + } + + public int ReadInt32() + { + return ReadNumber(bits: 32); + } + + // ReadToEnd through bits + + public byte[] ReadToEnd() + { + var remaining = Length - Position; + var result = new byte[(remaining + 7) / 8]; + for (var i = 0; i < remaining; i++) + result[i / 8] |= (byte)((ReadBit() ? 1 : 0) << (i % 8)); + return result; + } +} diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 67cba04be..282197958 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -1,7 +1,4 @@ -using System.Collections; -using System.Text; - -namespace GBX.NET.Engines.Game; +namespace GBX.NET.Engines.Game; // // Massive thanks to Mystixor and Shweetz for many of the findings!!! @@ -28,14 +25,14 @@ public enum EVersion private int u02; private TimeInt32? startOffset; private int ticks; - private byte[]? data; + private byte[] data = Array.Empty(); public EVersion Version { get => version; set => version = value; } public int U02 { get => u02; set => u02 = value; } public TimeInt32? StartOffset { get => startOffset; set => startOffset = value; } public int Ticks { get => ticks; set => ticks = value; } - public byte[]? Data { get => data; set => data = value; } - public IList InputChanges { get; set; } = Array.Empty(); + public byte[] Data { get => data; set => data = value; } + public IList InputChanges { get; set; } = Array.Empty(); public void ReadWrite(GameBoxReaderWriter rw, int version = 0) { @@ -48,18 +45,22 @@ public void ReadWrite(GameBoxReaderWriter rw, int version = 0) } rw.Int32(ref ticks); - rw.Bytes(ref data); + rw.Bytes(ref data!); - if (rw.Reader is null || data is null) + if (rw.Reader is null) { return; } - InputChanges = new List(); + InputChanges = new List(); try { - foreach (var inputChange in ProcessInputs(data, ticks)) + var inputChanges = Version is <= EVersion._2017_09_12 + ? ProcessShootmaniaInputs() + : ProcessTrackmaniaInputs(); + + foreach (var inputChange in inputChanges) { InputChanges.Add(inputChange); } @@ -70,127 +71,149 @@ public void ReadWrite(GameBoxReaderWriter rw, int version = 0) } } - internal static IEnumerable ProcessInputs(byte[] data, int ticks) + internal IEnumerable ProcessShootmaniaInputs() { - var bits = new BitArray(data); + var r = new BitReader(data); - /*var builder = new StringBuilder(); - for (var i = 0; i < bits.Length; i++) + for (var i = 0; i < ticks; i++) { - if (i != 0 && i % 8 == 0) + var different = false; + + var mouseAccuX = default(short?); + var mouseAccuY = default(short?); + var strafe = default(EStrafe?); + var walk = default(EWalk?); + var vertical = default(byte?); + var isAction = default(bool?); + var isGunTrigger = default(bool?); + var states = default(int?); + + var sameMouse = r.ReadBit(); + + if (!sameMouse) { - builder.Append(' '); + mouseAccuX = r.ReadInt16(); + mouseAccuY = r.ReadInt16(); + + different = true; } - if (i != 0 && i % 32 == 0) + + var sameValuesAfter = r.ReadBit(); + var sameValue = sameValuesAfter; + + if (!sameValuesAfter) { - builder.AppendLine(); + sameValue = r.ReadBit(); } - builder.Append(bits[i] ? "1" : "0"); - } - Console.WriteLine(builder.ToString());*/ - var position = 0; - - for (var i = 0; i < ticks; i++) - { - var somethingHasChanged = true; - var hasSteer = false; - var steer = default(sbyte); - var gas = default(bool?); - var brake = default(bool?); - var horn = default(bool?); - var isRespawn = false; - var zeroBitSet = false; - - for (var bit = 0; bit < 13; bit++) + if (!sameValue) { - if (position >= bits.Length) // Happens often with padding > 0, dunno why - { - break; - } + strafe = (EStrafe)r.Read2Bit(); + if (strafe == EStrafe.Corrupted) throw new Exception("Corrupted inputs. (strafe == 2)"); - var bitSet = bits.Get(position); - position++; + walk = (EWalk)r.Read2Bit(); + if (walk == EWalk.Corrupted) throw new Exception("Corrupted inputs. (walk == 2)"); - if (bit == 0) + if (version == EVersion._2017_09_12) { - zeroBitSet = bitSet; - - if (zeroBitSet) - { - continue; - } - - var isHorn = bits.Get(position); - var isMouse = isHorn && bits.Get(position); // mouse indicates 11 after the zero bit - - if (isMouse) - { - position += 31; - break; - } + vertical = r.Read2Bit(); + } - isRespawn = !isHorn; - var isRespawnHorn = isRespawn && position + 7 <= bits.Count && bits.Get(position + 7); + different = true; + } - if (isRespawnHorn) - { - horn = true; - } - else if (isHorn) - { - horn = bits.Get(position + 2); - } + if (!sameValuesAfter) + { + sameValue = r.ReadBit(); - position += isHorn ? 3 : 35; - break; - } - else if (bit == 1 && bitSet && zeroBitSet) + if (!sameValue) { - somethingHasChanged = false; - - var goToNextTick = bits.Get(position); + var onlyTriggerAndAction = r.ReadBit(); - if (goToNextTick) + if (onlyTriggerAndAction) { - position++; - break; // nothing has changed + isAction = r.ReadBit(); + isGunTrigger = r.ReadBit(); } - - bit = -1; // This line makes the different events merge into the same tick - continue; - } - else if (bit >= 2 && bit <= 9) - { - hasSteer = true; - - if (bitSet) + else { - steer |= (sbyte)(1 << (bit - 2)); + states = r.ReadInt32(); } - } - else if (bit == 10) - { - gas = bitSet; - } - else if (bit == 11) - { - brake = bitSet; - } - else if (bit == 12 && !bitSet) - { - bit = -1; // This line makes the different events merge into the same tick - position--; + + different = true; } } - if (somethingHasChanged) + if (different) { - yield return new(TimeInt32.FromMilliseconds(i * 10), hasSteer ? steer : null, gas, brake, horn, isRespawn); + yield return new ShootmaniaInputChange(i, mouseAccuX, mouseAccuY, strafe, walk, vertical, isAction, isGunTrigger, states); } } + + var theRest = r.ReadToEnd(); + + if (theRest.Any(x => x != 0)) + { + throw new Exception("Input buffer not cleared out completely"); + } + } + + internal IEnumerable ProcessTrackmaniaInputs() + { + yield break; } - public record struct InputChange(TimeInt32 Timestamp, sbyte? Steer, bool? Gas, bool? Brake, bool? Horn, bool IsRespawn); + public interface IInputChange + { + int Tick { get; } + TimeInt32 Timestamp { get; } + } + + public enum EStrafe : byte + { + None, + Left, + Corrupted, + Right + } + + public enum EWalk : byte + { + None, + Forward, + Corrupted, + Backward + } + + public record struct ShootmaniaInputChange(int Tick, + short? MouseAccuX, + short? MouseAccuY, + EStrafe? Strafe, + EWalk? Walk, + byte? Vertical, + bool? IsAction, + bool? IsGunTrigger, + int? States) : IInputChange + { + public TimeInt32 Timestamp => new(Tick * 10); + + public bool? FreeLook => States is null ? null : (States & 4) != 0; + public bool? Jump => States is null ? null : (States & 128) != 0; + public bool? Action => States is null ? null : (States & 257) != 0; + public bool? ActionSlot1 => States is null ? null : (States & 1024) != 0; + public bool? ActionSlot2 => States is null ? null : (States & 2048) != 0; + public bool? ActionSlot3 => States is null ? null : (States & 4096) != 0; + public bool? ActionSlot4 => States is null ? null : (States & 8192) != 0; + public bool? Use1 => States is null ? null : (States & 16896) != 0; + public bool? Use2 => States is null ? null : (States & 32832) != 0; + public bool? Menu => States is null ? null : (States & 65536) != 0; + public bool? Horn => States is null ? null : (States & 1048576) != 0; + public bool? Respawn => States is null ? null : (States & 2097152) != 0; + } + + public record struct TrackmaniaInputChange(int Tick) : IInputChange + { + public TimeInt32 Timestamp { get; } = new(Tick * 10); + } } } From a7590735d0dcdf6b9efd323afbab409575f13ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Wed, 28 Dec 2022 22:33:49 +0100 Subject: [PATCH 26/45] Add Camera2 --- Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 282197958..318ac62c0 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -198,6 +198,7 @@ public record struct ShootmaniaInputChange(int Tick, public TimeInt32 Timestamp => new(Tick * 10); public bool? FreeLook => States is null ? null : (States & 4) != 0; + public bool? Camera2 => States is null ? null : (States & 32) != 0; public bool? Jump => States is null ? null : (States & 128) != 0; public bool? Action => States is null ? null : (States & 257) != 0; public bool? ActionSlot1 => States is null ? null : (States & 1024) != 0; From d30dd63ed8de6d5048b7be46d97d29797fdb11f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Wed, 28 Dec 2022 22:39:45 +0100 Subject: [PATCH 27/45] Merge Action into IsAction --- .../Game/CGameCtnGhost.PlayerInputData.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 318ac62c0..682105f69 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -130,15 +130,9 @@ internal IEnumerable ProcessShootmaniaInputs() { var onlyTriggerAndAction = r.ReadBit(); - if (onlyTriggerAndAction) - { - isAction = r.ReadBit(); - isGunTrigger = r.ReadBit(); - } - else - { - states = r.ReadInt32(); - } + states = onlyTriggerAndAction + ? r.Read2Bit() + : r.ReadInt32(); different = true; } @@ -146,7 +140,7 @@ internal IEnumerable ProcessShootmaniaInputs() if (different) { - yield return new ShootmaniaInputChange(i, mouseAccuX, mouseAccuY, strafe, walk, vertical, isAction, isGunTrigger, states); + yield return new ShootmaniaInputChange(i, mouseAccuX, mouseAccuY, strafe, walk, vertical, states); } } @@ -191,16 +185,15 @@ public record struct ShootmaniaInputChange(int Tick, EStrafe? Strafe, EWalk? Walk, byte? Vertical, - bool? IsAction, - bool? IsGunTrigger, int? States) : IInputChange { public TimeInt32 Timestamp => new(Tick * 10); + public bool? IsGunTrigger => States is null ? null : (States & 2) != 0; public bool? FreeLook => States is null ? null : (States & 4) != 0; public bool? Camera2 => States is null ? null : (States & 32) != 0; public bool? Jump => States is null ? null : (States & 128) != 0; - public bool? Action => States is null ? null : (States & 257) != 0; + public bool? IsAction => States is null ? null : (States & 257) != 0; public bool? ActionSlot1 => States is null ? null : (States & 1024) != 0; public bool? ActionSlot2 => States is null ? null : (States & 2048) != 0; public bool? ActionSlot3 => States is null ? null : (States & 4096) != 0; From 1d0ae8a6ad003fdc29a5e6ab999dba6ad738e311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 03:55:10 +0100 Subject: [PATCH 28/45] Add Fly --- .../Engines/Game/CGameCtnGhost.PlayerInputData.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 682105f69..e9a0a4de5 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -84,8 +84,6 @@ internal IEnumerable ProcessShootmaniaInputs() var strafe = default(EStrafe?); var walk = default(EWalk?); var vertical = default(byte?); - var isAction = default(bool?); - var isGunTrigger = default(bool?); var states = default(int?); var sameMouse = r.ReadBit(); @@ -157,12 +155,6 @@ internal IEnumerable ProcessTrackmaniaInputs() yield break; } - public interface IInputChange - { - int Tick { get; } - TimeInt32 Timestamp { get; } - } - public enum EStrafe : byte { None, @@ -179,6 +171,12 @@ public enum EWalk : byte Backward } + public interface IInputChange + { + int Tick { get; } + TimeInt32 Timestamp { get; } + } + public record struct ShootmaniaInputChange(int Tick, short? MouseAccuX, short? MouseAccuY, @@ -191,6 +189,7 @@ public record struct ShootmaniaInputChange(int Tick, public bool? IsGunTrigger => States is null ? null : (States & 2) != 0; public bool? FreeLook => States is null ? null : (States & 4) != 0; + public bool? Fly => States is null ? null : (States & 8) != 0; public bool? Camera2 => States is null ? null : (States & 32) != 0; public bool? Jump => States is null ? null : (States & 128) != 0; public bool? IsAction => States is null ? null : (States & 257) != 0; From 23c2eb0bf1427e52fcccdfb453051604299f2d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 05:35:49 +0100 Subject: [PATCH 29/45] Add functional TM2020 inputerino (yes, with mouse) --- Src/GBX.NET/BitReader.cs | 4 +- .../Game/CGameCtnGhost.PlayerInputData.cs | 122 ++++++++++++++---- 2 files changed, 96 insertions(+), 30 deletions(-) diff --git a/Src/GBX.NET/BitReader.cs b/Src/GBX.NET/BitReader.cs index afbf2a29a..a3f088df8 100644 --- a/Src/GBX.NET/BitReader.cs +++ b/Src/GBX.NET/BitReader.cs @@ -21,7 +21,7 @@ public bool ReadBit() return result; } - public int ReadNumber(int bits) + public long ReadNumber(int bits) { var result = 0; for (var i = 0; i < bits; i++) @@ -51,7 +51,7 @@ public short ReadInt16() public int ReadInt32() { - return ReadNumber(bits: 32); + return (int)ReadNumber(bits: 32); } // ReadToEnd through bits diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index e9a0a4de5..b3c7fdeca 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -27,12 +27,30 @@ public enum EVersion private int ticks; private byte[] data = Array.Empty(); + private IList? inputChanges; + public EVersion Version { get => version; set => version = value; } public int U02 { get => u02; set => u02 = value; } public TimeInt32? StartOffset { get => startOffset; set => startOffset = value; } public int Ticks { get => ticks; set => ticks = value; } public byte[] Data { get => data; set => data = value; } - public IList InputChanges { get; set; } = Array.Empty(); + + public IList InputChanges + { + get + { + if (inputChanges is null) + { + var inputEnumerable = version is <= EVersion._2017_09_12 + ? ProcessShootmaniaInputs() + : ProcessTrackmaniaInputs(); + + inputChanges = inputEnumerable.ToArray(); + } + + return inputChanges; + } + } public void ReadWrite(GameBoxReaderWriter rw, int version = 0) { @@ -46,29 +64,6 @@ public void ReadWrite(GameBoxReaderWriter rw, int version = 0) rw.Int32(ref ticks); rw.Bytes(ref data!); - - if (rw.Reader is null) - { - return; - } - - InputChanges = new List(); - - try - { - var inputChanges = Version is <= EVersion._2017_09_12 - ? ProcessShootmaniaInputs() - : ProcessTrackmaniaInputs(); - - foreach (var inputChange in inputChanges) - { - InputChanges.Add(inputChange); - } - } - catch (Exception ex) - { - rw.Reader.Logger?.LogWarning(ex, "Error while reading inputs!"); - } } internal IEnumerable ProcessShootmaniaInputs() @@ -149,10 +144,71 @@ internal IEnumerable ProcessShootmaniaInputs() throw new Exception("Input buffer not cleared out completely"); } } - + internal IEnumerable ProcessTrackmaniaInputs() { - yield break; + var r = new BitReader(data); + + for (var i = 0; i < ticks; i++) + { + var different = false; + + var states = default(long?); + var mouseAccuX = default(short?); + var mouseAccuY = default(short?); + var steer = default(sbyte?); + var gas = default(bool?); + var brake = default(bool?); + + var sameState = r.ReadBit(); + + if (!sameState) + { + var onlySomething = r.ReadBit(); + + states = onlySomething + ? r.Read2Bit() + : r.ReadNumber(version is EVersion._2020_04_08 ? 33 : 34); + + different = true; + } + + var sameMouse = r.ReadBit(); + + if (!sameMouse) + { + mouseAccuX = r.ReadInt16(); + mouseAccuY = r.ReadInt16(); + + different = true; + } + + if (i > 0) // This check is a bit weird, may not work for StormMan gameplay + { + var sameValue = r.ReadBit(); + + if (!sameValue) + { + steer = r.ReadSByte(); + gas = r.ReadBit(); + brake = r.ReadBit(); + + different = true; + } + } + + if (different) + { + yield return new TrackmaniaInputChange(i, states, mouseAccuX, mouseAccuY, steer, gas, brake); + } + } + + var theRest = r.ReadToEnd(); + + if (theRest.Any(x => x != 0)) + { + throw new Exception("Input buffer not cleared out completely"); + } } public enum EStrafe : byte @@ -174,6 +230,10 @@ public enum EWalk : byte public interface IInputChange { int Tick { get; } + short? MouseAccuX { get; } + short? MouseAccuY { get; } + long? States { get; } + TimeInt32 Timestamp { get; } } @@ -183,7 +243,7 @@ public record struct ShootmaniaInputChange(int Tick, EStrafe? Strafe, EWalk? Walk, byte? Vertical, - int? States) : IInputChange + long? States) : IInputChange { public TimeInt32 Timestamp => new(Tick * 10); @@ -204,7 +264,13 @@ public record struct ShootmaniaInputChange(int Tick, public bool? Respawn => States is null ? null : (States & 2097152) != 0; } - public record struct TrackmaniaInputChange(int Tick) : IInputChange + public record struct TrackmaniaInputChange(int Tick, + long? States, + short? MouseAccuX, + short? MouseAccuY, + sbyte? Steer, + bool? Gas, + bool? Brake) : IInputChange { public TimeInt32 Timestamp { get; } = new(Tick * 10); } From 3453b84f99673c4ab490c4aea3e21856ed704bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 05:53:54 +0100 Subject: [PATCH 30/45] Update weird check --- .../Engines/Game/CGameCtnGhost.PlayerInputData.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index b3c7fdeca..45d60dc58 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -183,7 +183,7 @@ internal IEnumerable ProcessTrackmaniaInputs() different = true; } - if (i > 0) // This check is a bit weird, may not work for StormMan gameplay + if (i > 0 || sameMouse) // This check is a bit weird, may not work for StormMan gameplay { var sameValue = r.ReadBit(); @@ -202,13 +202,6 @@ internal IEnumerable ProcessTrackmaniaInputs() yield return new TrackmaniaInputChange(i, states, mouseAccuX, mouseAccuY, steer, gas, brake); } } - - var theRest = r.ReadToEnd(); - - if (theRest.Any(x => x != 0)) - { - throw new Exception("Input buffer not cleared out completely"); - } } public enum EStrafe : byte From 2be6d9a1bfcc197e696406fdd617dec72de135e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 06:30:33 +0100 Subject: [PATCH 31/45] Little fixing --- Src/GBX.NET/BitReader.cs | 6 +-- .../Game/CGameCtnGhost.PlayerInputData.cs | 41 +++++++++++++++---- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Src/GBX.NET/BitReader.cs b/Src/GBX.NET/BitReader.cs index a3f088df8..d6a8cdedc 100644 --- a/Src/GBX.NET/BitReader.cs +++ b/Src/GBX.NET/BitReader.cs @@ -21,11 +21,11 @@ public bool ReadBit() return result; } - public long ReadNumber(int bits) + public ulong ReadNumber(int bits) { - var result = 0; + ulong result = 0; for (var i = 0; i < bits; i++) - result |= (ReadBit() ? 1 : 0) << i; + result |= (ulong)(ReadBit() ? 1 : 0) << i; return result; } diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 45d60dc58..4cfdebe79 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -3,6 +3,7 @@ // // Massive thanks to Mystixor and Shweetz for many of the findings!!! // Without these guys, this would've stayed a mystery. +// Even though the solution was read out of the game code in the end, they gave the important hint. // public partial class CGameCtnGhost @@ -10,7 +11,6 @@ public partial class CGameCtnGhost /// /// Set of inputs. /// - /// Massive thanks to Mystixor and Shweetz for many of the findings!!! public class PlayerInputData : IReadableWritable { public enum EVersion @@ -153,7 +153,7 @@ internal IEnumerable ProcessTrackmaniaInputs() { var different = false; - var states = default(long?); + var states = default(ulong?); var mouseAccuX = default(short?); var mouseAccuY = default(short?); var steer = default(sbyte?); @@ -168,7 +168,7 @@ internal IEnumerable ProcessTrackmaniaInputs() states = onlySomething ? r.Read2Bit() - : r.ReadNumber(version is EVersion._2020_04_08 ? 33 : 34); + : r.ReadNumber(bits: version is EVersion._2020_04_08 ? 33 : 34); different = true; } @@ -183,7 +183,11 @@ internal IEnumerable ProcessTrackmaniaInputs() different = true; } - if (i > 0 || sameMouse) // This check is a bit weird, may not work for StormMan gameplay + // This check is a bit weird, may not work for StormMan gameplay + // If starting with horn on, it is included on first tick + // If mouse is not plugged, it is also included + // In code, this check is presented as '(X - 2 & 0xfffffffd) == 0' + if ((i == 0 && ((states.GetValueOrDefault() & 64) != 0)) || sameMouse) { var sameValue = r.ReadBit(); @@ -202,6 +206,13 @@ internal IEnumerable ProcessTrackmaniaInputs() yield return new TrackmaniaInputChange(i, states, mouseAccuX, mouseAccuY, steer, gas, brake); } } + + var theRest = r.ReadToEnd(); + + if (theRest.Any(x => x != 0)) + { + throw new Exception("Input buffer not cleared out completely"); + } } public enum EStrafe : byte @@ -225,7 +236,7 @@ public interface IInputChange int Tick { get; } short? MouseAccuX { get; } short? MouseAccuY { get; } - long? States { get; } + ulong? States { get; } TimeInt32 Timestamp { get; } } @@ -236,7 +247,7 @@ public record struct ShootmaniaInputChange(int Tick, EStrafe? Strafe, EWalk? Walk, byte? Vertical, - long? States) : IInputChange + int? States) : IInputChange { public TimeInt32 Timestamp => new(Tick * 10); @@ -255,10 +266,12 @@ public record struct ShootmaniaInputChange(int Tick, public bool? Menu => States is null ? null : (States & 65536) != 0; public bool? Horn => States is null ? null : (States & 1048576) != 0; public bool? Respawn => States is null ? null : (States & 2097152) != 0; + + ulong? IInputChange.States => States is null ? null : (ulong)States; } public record struct TrackmaniaInputChange(int Tick, - long? States, + ulong? States, short? MouseAccuX, short? MouseAccuY, sbyte? Steer, @@ -266,6 +279,20 @@ public record struct TrackmaniaInputChange(int Tick, bool? Brake) : IInputChange { public TimeInt32 Timestamp { get; } = new(Tick * 10); + + public bool? Respawn => States is null ? null : (States & 2147483648) != 0; + public bool? Horn + { + get + { + if (States is null) + { + return null; + } + + return (States & (ulong)(Tick == 0 ? 64 : 2)) != 0; + } + } } } } From 6098a95f832b6e5eef2cefd153e86bd67b28add9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 07:18:30 +0100 Subject: [PATCH 32/45] Final fixes for now --- .../Game/CGameCtnGhost.PlayerInputData.cs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 4cfdebe79..4977599fd 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -187,7 +187,8 @@ internal IEnumerable ProcessTrackmaniaInputs() // If starting with horn on, it is included on first tick // If mouse is not plugged, it is also included // In code, this check is presented as '(X - 2 & 0xfffffffd) == 0' - if ((i == 0 && ((states.GetValueOrDefault() & 64) != 0)) || sameMouse) + + if (states.HasValue || i > 0) { var sameValue = r.ReadBit(); @@ -281,18 +282,7 @@ public record struct TrackmaniaInputChange(int Tick, public TimeInt32 Timestamp { get; } = new(Tick * 10); public bool? Respawn => States is null ? null : (States & 2147483648) != 0; - public bool? Horn - { - get - { - if (States is null) - { - return null; - } - - return (States & (ulong)(Tick == 0 ? 64 : 2)) != 0; - } - } + public bool? Horn => null; } } } From 1dfb119a0ec00082a1f445d7334a7d7f2fc3ce84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 07:41:03 +0100 Subject: [PATCH 33/45] Adjust check --- Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 4977599fd..dd1133d3b 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -188,7 +188,7 @@ internal IEnumerable ProcessTrackmaniaInputs() // If mouse is not plugged, it is also included // In code, this check is presented as '(X - 2 & 0xfffffffd) == 0' - if (states.HasValue || i > 0) + if (states.HasValue || i > 1) { var sameValue = r.ReadBit(); From cb401e31cc0458c213eeddac24c4cc4472637422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 07:47:53 +0100 Subject: [PATCH 34/45] Revert --- Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index dd1133d3b..4977599fd 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -188,7 +188,7 @@ internal IEnumerable ProcessTrackmaniaInputs() // If mouse is not plugged, it is also included // In code, this check is presented as '(X - 2 & 0xfffffffd) == 0' - if (states.HasValue || i > 1) + if (states.HasValue || i > 0) { var sameValue = r.ReadBit(); From b559185824166a680c1693ac4d0048da569c5f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 16:56:42 +0100 Subject: [PATCH 35/45] Fix more weird scenarios --- .../Game/CGameCtnGhost.PlayerInputData.cs | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 4977599fd..23195750f 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -45,6 +45,22 @@ public IList InputChanges ? ProcessShootmaniaInputs() : ProcessTrackmaniaInputs(); +#if DEBUG + var testList = new List(); + + try + { + foreach (var input in inputEnumerable) + { + testList.Add(input); + } + } + catch + { + + } +#endif + inputChanges = inputEnumerable.ToArray(); } @@ -149,6 +165,8 @@ internal IEnumerable ProcessTrackmaniaInputs() { var r = new BitReader(data); + var started = false; + for (var i = 0; i < ticks; i++) { var different = false; @@ -170,6 +188,11 @@ internal IEnumerable ProcessTrackmaniaInputs() ? r.Read2Bit() : r.ReadNumber(bits: version is EVersion._2020_04_08 ? 33 : 34); + if (!started) + { + started = (states & 2) != 0; + } + different = true; } @@ -188,7 +211,12 @@ internal IEnumerable ProcessTrackmaniaInputs() // If mouse is not plugged, it is also included // In code, this check is presented as '(X - 2 & 0xfffffffd) == 0' - if (states.HasValue || i > 0) + if (r.Position < r.Length) + { + yield break; + } + + if (started) { var sameValue = r.ReadBit(); From 7a2a9ef71984bc337a601fd82cc7d0feba2f72cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 16:57:02 +0100 Subject: [PATCH 36/45] Add CGameCtnGhost 0x02E --- Src/GBX.NET/Engines/Game/CGameCtnGhost.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.cs index 039e0610c..c8c205b89 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.cs @@ -1343,5 +1343,18 @@ public class Chunk0309202D : SkippableChunk #endregion + #region 0x02E skippable chunk + + /// + /// CGameCtnGhost 0x02E skippable chunk + /// + [Chunk(0x0309202E), IgnoreChunk] + public class Chunk0309202E : SkippableChunk + { + + } + + #endregion + #endregion } From 0d19faecce33442a1383fbc76055f612358a2744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 16:57:24 +0100 Subject: [PATCH 37/45] Add CGameCtnMediaBlockTriangles 0x002 --- .../Game/CGameCtnMediaBlockTriangles.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.cs b/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.cs index 2b22cd019..4b6524a2e 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnMediaBlockTriangles.cs @@ -185,6 +185,24 @@ public override void Write(CGameCtnMediaBlockTriangles n, GameBoxWriter w) } } + #region 0x002 chunk + + /// + /// CGameCtnMediaBlockTriangles 0x002 skippable chunk + /// + [Chunk(0x03029002)] + public class Chunk03029002 : SkippableChunk + { + public int U01; + + public override void ReadWrite(CGameCtnMediaBlockTriangles n, GameBoxReaderWriter rw) + { + rw.Int32(ref U01); + } + } + + #endregion + #endregion #endregion From bceaffa5045879b14dc79eced0f55cd4c4062622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 16:57:47 +0100 Subject: [PATCH 38/45] Improve ReadNumber performance --- Src/GBX.NET/BitReader.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Src/GBX.NET/BitReader.cs b/Src/GBX.NET/BitReader.cs index d6a8cdedc..26b698da5 100644 --- a/Src/GBX.NET/BitReader.cs +++ b/Src/GBX.NET/BitReader.cs @@ -24,8 +24,15 @@ public bool ReadBit() public ulong ReadNumber(int bits) { ulong result = 0; + + var dataSpan = data.AsSpan(); + for (var i = 0; i < bits; i++) - result |= (ulong)(ReadBit() ? 1 : 0) << i; + { + result |= (ulong)(dataSpan[Position / 8] & (1 << (Position % 8))) << i; + Position++; + } + return result; } From 1caf09b185afc6e063e8c713035544855426df24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 19:11:19 +0100 Subject: [PATCH 39/45] Fix random lack of ticks in data --- .../Game/CGameCtnGhost.PlayerInputData.cs | 80 ++++++++++--------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 23195750f..381d2d294 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -178,57 +178,59 @@ internal IEnumerable ProcessTrackmaniaInputs() var gas = default(bool?); var brake = default(bool?); - var sameState = r.ReadBit(); - - if (!sameState) + try { - var onlySomething = r.ReadBit(); - - states = onlySomething - ? r.Read2Bit() - : r.ReadNumber(bits: version is EVersion._2020_04_08 ? 33 : 34); + var sameState = r.ReadBit(); - if (!started) + if (!sameState) { - started = (states & 2) != 0; - } - - different = true; - } + var onlySomething = r.ReadBit(); - var sameMouse = r.ReadBit(); + states = onlySomething + ? r.Read2Bit() + : r.ReadNumber(bits: version is EVersion._2020_04_08 ? 33 : 34); - if (!sameMouse) - { - mouseAccuX = r.ReadInt16(); - mouseAccuY = r.ReadInt16(); + if (!started) + { + started = (states & 2) != 0; + } - different = true; - } + different = true; + } - // This check is a bit weird, may not work for StormMan gameplay - // If starting with horn on, it is included on first tick - // If mouse is not plugged, it is also included - // In code, this check is presented as '(X - 2 & 0xfffffffd) == 0' + var sameMouse = r.ReadBit(); - if (r.Position < r.Length) - { - yield break; - } + if (!sameMouse) + { + mouseAccuX = r.ReadInt16(); + mouseAccuY = r.ReadInt16(); - if (started) - { - var sameValue = r.ReadBit(); + different = true; + } - if (!sameValue) + // This check is a bit weird, may not work for StormMan gameplay + // If starting with horn on, it is included on first tick + // If mouse is not plugged, it is also included + // In code, this check is presented as '(X - 2 & 0xfffffffd) == 0' + + if (started) { - steer = r.ReadSByte(); - gas = r.ReadBit(); - brake = r.ReadBit(); + var sameValue = r.ReadBit(); - different = true; + if (!sameValue) + { + steer = r.ReadSByte(); + gas = r.ReadBit(); + brake = r.ReadBit(); + + different = true; + } } } + catch (IndexOutOfRangeException) + { + + } if (different) { @@ -270,7 +272,7 @@ public interface IInputChange TimeInt32 Timestamp { get; } } - public record struct ShootmaniaInputChange(int Tick, + public readonly record struct ShootmaniaInputChange(int Tick, short? MouseAccuX, short? MouseAccuY, EStrafe? Strafe, @@ -299,7 +301,7 @@ public record struct ShootmaniaInputChange(int Tick, ulong? IInputChange.States => States is null ? null : (ulong)States; } - public record struct TrackmaniaInputChange(int Tick, + public readonly record struct TrackmaniaInputChange(int Tick, ulong? States, short? MouseAccuX, short? MouseAccuY, From 4d2a844b056049ba1d7a8f35f3bc76c08e5dfc56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 19:11:59 +0100 Subject: [PATCH 40/45] Improve ReadNumber performance --- Src/GBX.NET/BitReader.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Src/GBX.NET/BitReader.cs b/Src/GBX.NET/BitReader.cs index 26b698da5..300d5ba99 100644 --- a/Src/GBX.NET/BitReader.cs +++ b/Src/GBX.NET/BitReader.cs @@ -14,23 +14,27 @@ public BitReader(byte[] data) Length = data.Length * 8; } - public bool ReadBit() + private bool ReadBit(ReadOnlySpan data) { var result = (data[Position / 8] & (1 << (Position % 8))) != 0; Position++; return result; } + public bool ReadBit() + { + return ReadBit(data); + } + public ulong ReadNumber(int bits) { ulong result = 0; - var dataSpan = data.AsSpan(); - + ReadOnlySpan dataSpan = data; + for (var i = 0; i < bits; i++) { - result |= (ulong)(dataSpan[Position / 8] & (1 << (Position % 8))) << i; - Position++; + result |= (ulong)(ReadBit(dataSpan) ? 1 : 0) << i; } return result; From ea68e78438684f51424c31307c86f7698560c0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 19:57:15 +0100 Subject: [PATCH 41/45] Add roughly functional horn --- .../Game/CGameCtnGhost.PlayerInputData.cs | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 381d2d294..4390c8761 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -273,12 +273,12 @@ public interface IInputChange } public readonly record struct ShootmaniaInputChange(int Tick, - short? MouseAccuX, - short? MouseAccuY, - EStrafe? Strafe, - EWalk? Walk, - byte? Vertical, - int? States) : IInputChange + short? MouseAccuX, + short? MouseAccuY, + EStrafe? Strafe, + EWalk? Walk, + byte? Vertical, + int? States) : IInputChange { public TimeInt32 Timestamp => new(Tick * 10); @@ -302,17 +302,33 @@ public readonly record struct ShootmaniaInputChange(int Tick, } public readonly record struct TrackmaniaInputChange(int Tick, - ulong? States, - short? MouseAccuX, - short? MouseAccuY, - sbyte? Steer, - bool? Gas, - bool? Brake) : IInputChange + ulong? States, + short? MouseAccuX, + short? MouseAccuY, + sbyte? Steer, + bool? Gas, + bool? Brake) : IInputChange { public TimeInt32 Timestamp { get; } = new(Tick * 10); public bool? Respawn => States is null ? null : (States & 2147483648) != 0; - public bool? Horn => null; + public bool? Horn + { + get + { + if (States is null) + { + return null; + } + + if (Tick < 2) + { + return null; // TODO + } + + return (States & 2) != 0; + } + } } } } From 77e9e094f2bd40251636d3a41d15960064200e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 20:40:54 +0100 Subject: [PATCH 42/45] Improve horn a bit more --- Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 4390c8761..585426127 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -326,7 +326,7 @@ public bool? Horn return null; // TODO } - return (States & 2) != 0; + return States == 2; } } } From 3b63f23a46fa811f18eb599b9708b1cac8d2e3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Thu, 29 Dec 2022 22:03:35 +0100 Subject: [PATCH 43/45] Make MouseAccu unsigned --- Src/GBX.NET/BitReader.cs | 5 +++ .../Game/CGameCtnGhost.PlayerInputData.cs | 34 +++++++++---------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Src/GBX.NET/BitReader.cs b/Src/GBX.NET/BitReader.cs index 300d5ba99..39c4311ae 100644 --- a/Src/GBX.NET/BitReader.cs +++ b/Src/GBX.NET/BitReader.cs @@ -60,6 +60,11 @@ public short ReadInt16() return (short)ReadNumber(bits: 16); } + public ushort ReadUInt16() + { + return (ushort)ReadNumber(bits: 16); + } + public int ReadInt32() { return (int)ReadNumber(bits: 32); diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 585426127..77596c666 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -90,8 +90,8 @@ internal IEnumerable ProcessShootmaniaInputs() { var different = false; - var mouseAccuX = default(short?); - var mouseAccuY = default(short?); + var mouseAccuX = default(ushort?); + var mouseAccuY = default(ushort?); var strafe = default(EStrafe?); var walk = default(EWalk?); var vertical = default(byte?); @@ -101,8 +101,8 @@ internal IEnumerable ProcessShootmaniaInputs() if (!sameMouse) { - mouseAccuX = r.ReadInt16(); - mouseAccuY = r.ReadInt16(); + mouseAccuX = r.ReadUInt16(); + mouseAccuY = r.ReadUInt16(); different = true; } @@ -172,8 +172,8 @@ internal IEnumerable ProcessTrackmaniaInputs() var different = false; var states = default(ulong?); - var mouseAccuX = default(short?); - var mouseAccuY = default(short?); + var mouseAccuX = default(ushort?); + var mouseAccuY = default(ushort?); var steer = default(sbyte?); var gas = default(bool?); var brake = default(bool?); @@ -202,8 +202,8 @@ internal IEnumerable ProcessTrackmaniaInputs() if (!sameMouse) { - mouseAccuX = r.ReadInt16(); - mouseAccuY = r.ReadInt16(); + mouseAccuX = r.ReadUInt16(); + mouseAccuY = r.ReadUInt16(); different = true; } @@ -229,7 +229,7 @@ internal IEnumerable ProcessTrackmaniaInputs() } catch (IndexOutOfRangeException) { - + } if (different) @@ -265,16 +265,16 @@ public enum EWalk : byte public interface IInputChange { int Tick { get; } - short? MouseAccuX { get; } - short? MouseAccuY { get; } + ushort? MouseAccuX { get; } + ushort? MouseAccuY { get; } ulong? States { get; } TimeInt32 Timestamp { get; } } public readonly record struct ShootmaniaInputChange(int Tick, - short? MouseAccuX, - short? MouseAccuY, + ushort? MouseAccuX, + ushort? MouseAccuY, EStrafe? Strafe, EWalk? Walk, byte? Vertical, @@ -303,8 +303,8 @@ public readonly record struct ShootmaniaInputChange(int Tick, public readonly record struct TrackmaniaInputChange(int Tick, ulong? States, - short? MouseAccuX, - short? MouseAccuY, + ushort? MouseAccuX, + ushort? MouseAccuY, sbyte? Steer, bool? Gas, bool? Brake) : IInputChange @@ -320,12 +320,12 @@ public bool? Horn { return null; } - + if (Tick < 2) { return null; // TODO } - + return States == 2; } } From 2a1f1e9a023b8d242e4f218dae63ad3344016646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Fri, 30 Dec 2022 03:35:00 +0100 Subject: [PATCH 44/45] Add Respawn and Horn to IInputChange --- Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs index 77596c666..97c06a2fd 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnGhost.PlayerInputData.cs @@ -268,6 +268,8 @@ public interface IInputChange ushort? MouseAccuX { get; } ushort? MouseAccuY { get; } ulong? States { get; } + bool? Respawn { get; } + bool? Horn { get; } TimeInt32 Timestamp { get; } } From b9c380c5abbfbc2c09faa754ad3e35146605dd78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pivo=C5=88ka?= Date: Fri, 30 Dec 2022 15:27:08 +0100 Subject: [PATCH 45/45] Update GBX.NET.PAK to 1.1.2 --- Src/GBX.NET.PAK/GBX.NET.PAK.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/GBX.NET.PAK/GBX.NET.PAK.csproj b/Src/GBX.NET.PAK/GBX.NET.PAK.csproj index 14927fcf7..9f84f1ca9 100644 --- a/Src/GBX.NET.PAK/GBX.NET.PAK.csproj +++ b/Src/GBX.NET.PAK/GBX.NET.PAK.csproj @@ -13,7 +13,7 @@ enable true - 1.1.1 + 1.1.2 net7.0;net6.0;netstandard2.1;netstandard2.0;net462