From a21b12ffd4a79afc5c2859eb080a4a95e8a3a565 Mon Sep 17 00:00:00 2001 From: Ammar Askar Date: Thu, 3 Oct 2024 23:38:44 -0400 Subject: [PATCH] Start adding codecs for OBJM and XOBJ files --- .../OpenTS2/Content/DBPF/ObjectModuleAsset.cs | 10 ++ .../Content/DBPF/ObjectModuleAsset.cs.meta | 3 + .../OpenTS2/Content/DBPF/SimsObjectAsset.cs | 7 + .../Content/DBPF/SimsObjectAsset.cs.meta | 3 + .../Files/Formats/DBPF/ObjectModuleCodec.cs | 54 ++++++ .../Formats/DBPF/ObjectModuleCodec.cs.meta | 3 + .../Formats/DBPF/ObjectSaveTypeTableCodec.cs | 1 + .../Files/Formats/DBPF/SimsObjectCodec.cs | 155 ++++++++++++++++++ .../Formats/DBPF/SimsObjectCodec.cs.meta | 3 + .../Formats/DBPF/ObjectModuleCodecTest.cs | 26 +++ .../DBPF/ObjectModuleCodecTest.cs.meta | 3 + .../Files/Formats/DBPF/SimsObjectCodecTest.cs | 26 +++ .../Formats/DBPF/SimsObjectCodecTest.cs.meta | 3 + 13 files changed, 297 insertions(+) create mode 100644 Assets/Scripts/OpenTS2/Content/DBPF/ObjectModuleAsset.cs create mode 100644 Assets/Scripts/OpenTS2/Content/DBPF/ObjectModuleAsset.cs.meta create mode 100644 Assets/Scripts/OpenTS2/Content/DBPF/SimsObjectAsset.cs create mode 100644 Assets/Scripts/OpenTS2/Content/DBPF/SimsObjectAsset.cs.meta create mode 100644 Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectModuleCodec.cs create mode 100644 Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectModuleCodec.cs.meta create mode 100644 Assets/Scripts/OpenTS2/Files/Formats/DBPF/SimsObjectCodec.cs create mode 100644 Assets/Scripts/OpenTS2/Files/Formats/DBPF/SimsObjectCodec.cs.meta create mode 100644 Assets/Tests/OpenTS2/Files/Formats/DBPF/ObjectModuleCodecTest.cs create mode 100644 Assets/Tests/OpenTS2/Files/Formats/DBPF/ObjectModuleCodecTest.cs.meta create mode 100644 Assets/Tests/OpenTS2/Files/Formats/DBPF/SimsObjectCodecTest.cs create mode 100644 Assets/Tests/OpenTS2/Files/Formats/DBPF/SimsObjectCodecTest.cs.meta diff --git a/Assets/Scripts/OpenTS2/Content/DBPF/ObjectModuleAsset.cs b/Assets/Scripts/OpenTS2/Content/DBPF/ObjectModuleAsset.cs new file mode 100644 index 00000000..5fffbf76 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Content/DBPF/ObjectModuleAsset.cs @@ -0,0 +1,10 @@ +namespace OpenTS2.Content.DBPF +{ + /// + /// Called cEdithObjectModule in game. + /// + public class ObjectModuleAsset : AbstractAsset + { + + } +} \ No newline at end of file diff --git a/Assets/Scripts/OpenTS2/Content/DBPF/ObjectModuleAsset.cs.meta b/Assets/Scripts/OpenTS2/Content/DBPF/ObjectModuleAsset.cs.meta new file mode 100644 index 00000000..55092758 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Content/DBPF/ObjectModuleAsset.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 88d31fc370514416b28927df14c0e41d +timeCreated: 1727972177 \ No newline at end of file diff --git a/Assets/Scripts/OpenTS2/Content/DBPF/SimsObjectAsset.cs b/Assets/Scripts/OpenTS2/Content/DBPF/SimsObjectAsset.cs new file mode 100644 index 00000000..ee876dde --- /dev/null +++ b/Assets/Scripts/OpenTS2/Content/DBPF/SimsObjectAsset.cs @@ -0,0 +1,7 @@ +namespace OpenTS2.Content.DBPF +{ + public class SimsObjectAsset : AbstractAsset + { + + } +} \ No newline at end of file diff --git a/Assets/Scripts/OpenTS2/Content/DBPF/SimsObjectAsset.cs.meta b/Assets/Scripts/OpenTS2/Content/DBPF/SimsObjectAsset.cs.meta new file mode 100644 index 00000000..f5d0caea --- /dev/null +++ b/Assets/Scripts/OpenTS2/Content/DBPF/SimsObjectAsset.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ac6c2c1af0484515b3e9c91dc91159dc +timeCreated: 1727122236 \ No newline at end of file diff --git a/Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectModuleCodec.cs b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectModuleCodec.cs new file mode 100644 index 00000000..da0271fb --- /dev/null +++ b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectModuleCodec.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; +using OpenTS2.Common; +using OpenTS2.Content; +using OpenTS2.Content.DBPF; +using OpenTS2.Files.Utils; +using UnityEngine; + +namespace OpenTS2.Files.Formats.DBPF +{ + /// + /// Codec for what is known in game as cEdithObjectModule. Likely a container for different types of objects. + /// + [Codec(TypeIDs.OBJM)] + public class ObjectModuleCodec : AbstractCodec + { + public override AbstractAsset Deserialize(byte[] bytes, ResourceKey tgi, DBPFFile sourceFile) + { + var asset = new ObjectModuleAsset(); + var stream = new MemoryStream(bytes); + var reader = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN); + + // Skip first 64 bytes. + reader.Seek(SeekOrigin.Begin, 64); + + // First int, ignored. + reader.Seek(SeekOrigin.Current, 4); + // Version number. + int version = reader.ReadInt32(); + Debug.Log($"Version: 0x{version:X}"); + // Type identifier. + uint type = reader.ReadUInt32(); + if (type != 0x4F626A4D) + { + // Corresponds to the string "ObjM" + throw new NotImplementedException("ObjM file does not have `ObjM` magic bytes"); + } + + // Next is the number of objects. + int numObjects = reader.ReadInt32(); + Debug.Log($"numObjects: {numObjects}"); + for (var i = 0; i < numObjects; i++) + { + int selectorSaveType = reader.ReadInt32(); + int missingObjectSaveType = reader.ReadInt32(); + + Debug.Log($"selectorSaveType: {selectorSaveType}, missingObjectSaveType: {missingObjectSaveType}"); + break; + } + + return asset; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectModuleCodec.cs.meta b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectModuleCodec.cs.meta new file mode 100644 index 00000000..f924f397 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectModuleCodec.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a49ebb5f202a4e24a7d135e40c194bc8 +timeCreated: 1727969785 \ No newline at end of file diff --git a/Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectSaveTypeTableCodec.cs b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectSaveTypeTableCodec.cs index ef045f10..0fb903a3 100644 --- a/Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectSaveTypeTableCodec.cs +++ b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/ObjectSaveTypeTableCodec.cs @@ -5,6 +5,7 @@ using OpenTS2.Content; using OpenTS2.Content.DBPF; using OpenTS2.Files.Utils; +using UnityEngine; namespace OpenTS2.Files.Formats.DBPF { diff --git a/Assets/Scripts/OpenTS2/Files/Formats/DBPF/SimsObjectCodec.cs b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/SimsObjectCodec.cs new file mode 100644 index 00000000..fc2a12b1 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/SimsObjectCodec.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.IO; +using OpenTS2.Common; +using OpenTS2.Content; +using OpenTS2.Content.DBPF; +using OpenTS2.Files.Utils; +using UnityEngine; + +namespace OpenTS2.Files.Formats.DBPF +{ + /// + /// Codec for what is known in game as cTSObject. Contains attributes and locations of objects. + /// + [Codec(TypeIDs.XOBJ)] + public class SimsObjectCodec : AbstractCodec + { + public override AbstractAsset Deserialize(byte[] bytes, ResourceKey tgi, DBPFFile sourceFile) + { + var asset = new SimsObjectAsset(); + var stream = new MemoryStream(bytes); + var reader = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN); + + // Skip first 64 bytes. + reader.Seek(SeekOrigin.Begin, 64); + + // TODO: this is for versions = 0xAD, see if we need to handle lower. + + // 4 skipped/unused floats + for (int i = 0; i < 4; i++) + { + reader.ReadFloat(); + } + + var tileLocationY = reader.ReadFloat(); + var tileLocationX = reader.ReadFloat(); + var level = reader.ReadInt32(); + // ignored int16 + reader.ReadUInt16(); + var elevation = reader.ReadFloat(); + var objectGroupId = reader.ReadInt32(); + var unknown = reader.ReadInt16(); + + Debug.Log($"tileLocationY: {tileLocationY}, tileLocationX: {tileLocationX}, level: {level}, " + + $"elevation: {elevation}, objectGroupId: {objectGroupId}, unknown: {unknown}"); + + var numAttrs = reader.ReadInt16(); + var attrs = new short[numAttrs]; + for (var i = 0; i < numAttrs; i++) + { + attrs[i] = reader.ReadInt16(); + } + Debug.Log($"numAttrs: {numAttrs}, attrs: [{string.Join(", ", attrs)}]"); + + var numSemiAttrs = reader.ReadInt16(); + var semiAttrs = new short[numSemiAttrs]; + for (var i = 0; i < numSemiAttrs; i++) + { + semiAttrs[i] = reader.ReadInt16(); + } + Debug.Log($"numSemiAttrs: {numAttrs}, semiAttrs: [{string.Join(", ", semiAttrs)}]"); + + Debug.Log($" Data array offset: {reader.Position:X}"); + + /* + // 8 unknown shorts called "data". + var dataArray = new short[8]; + for (var i = 0; i < dataArray.Length; i++) + { + dataArray[i] = reader.ReadInt16(); + } + Debug.Log($"dataArray: [{string.Join(", ", dataArray)}]"); + + // Next is a number of shorts that depends on the exact version of the file. + uint numShorts = 0x57 + 6; + for (var i = 0; i < numShorts; i++) + { + reader.ReadInt16(); + } + + // Another 8 shorts, called the "tempTokenFields". + for (var i = 0; i < 8; i++) + { + reader.ReadInt16(); + } + + // Next is the number of object arrays. Each being a short array itself. + var numObjectArrays = reader.ReadInt16(); + var shortArrays = new List(numObjectArrays); + for (var i = 0; i < numObjectArrays; i++) + { + var objectArray = new short[reader.ReadInt16()]; + for (var j = 0; j < objectArray.Length; j++) + { + objectArray[j] = reader.ReadInt16(); + } + shortArrays.Add(objectArray); + } + Debug.Log($"numObjectArrays: {numObjectArrays}"); + + // An array of shorts. Unknown. + var numSecondShortArray = reader.ReadInt16(); + for (var i = 0; i < numSecondShortArray; i++) + { + reader.ReadInt16(); + } + Debug.Log($"numSecondShortArrays: {numSecondShortArray}"); + + var ownershipValue = reader.ReadInt32(); + Debug.Log($"ownershipValue: {ownershipValue}"); + + Debug.Log($" Position before strings: 0x{reader.Position:X}"); + // A number of material subsitution strings. + var numMaterialSubstitues = reader.ReadInt16(); + Debug.Log($" numMaterialSubstitues: {numMaterialSubstitues}"); + for (var i = 0; i < numMaterialSubstitues; i++) + { + var materialSubstitute = reader.ReadVariableLengthPascalString(); + Debug.Log($"materialSubstitute: {materialSubstitute}"); + } + + var persistentFlag = reader.ReadUInt16(); + Debug.Log($"persistentFlag: {persistentFlag}"); + + // Slots... + var slotsFlag = reader.ReadInt16(); + Debug.Log($"slotsFlag: {slotsFlag}"); + + var numSlots = reader.ReadInt16(); + for (var i = 0; i < numSlots; i++) + { + var slotValue = reader.ReadInt16(); + } + Debug.Log($"numSlots: {numSlots}"); + + var numEffects = reader.ReadInt16(); + Debug.Log($"numEffects: {numEffects}"); + + Debug.Log($"Position after numEffects: 0x{reader.Position:X}"); + + var numbOverrides = reader.ReadInt16(); + Debug.Log($"numOverides: {numbOverrides}"); + + for (int i = 0; i < numbOverrides; i++) + { + var overrideString1 = reader.ReadVariableLengthPascalString(); + var overrideString2 = reader.ReadVariableLengthPascalString(); + var overrideString3 = reader.ReadVariableLengthPascalString(); + Debug.Log($"{overrideString1} / {overrideString2} / {overrideString3}"); + }*/ + + return asset; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/OpenTS2/Files/Formats/DBPF/SimsObjectCodec.cs.meta b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/SimsObjectCodec.cs.meta new file mode 100644 index 00000000..45954160 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/SimsObjectCodec.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 56e83332c7cd425b96ef041bca7ee9fc +timeCreated: 1727121939 \ No newline at end of file diff --git a/Assets/Tests/OpenTS2/Files/Formats/DBPF/ObjectModuleCodecTest.cs b/Assets/Tests/OpenTS2/Files/Formats/DBPF/ObjectModuleCodecTest.cs new file mode 100644 index 00000000..546a9393 --- /dev/null +++ b/Assets/Tests/OpenTS2/Files/Formats/DBPF/ObjectModuleCodecTest.cs @@ -0,0 +1,26 @@ +using NUnit.Framework; +using OpenTS2.Common; +using OpenTS2.Content; +using OpenTS2.Content.DBPF; +using OpenTS2.Files.Formats.DBPF; + +public class ObjectModuleCodecTest +{ + private uint _groupID; + + [SetUp] + public void SetUp() + { + TestMain.Initialize(); + _groupID = ContentProvider.Get().AddPackage("TestAssets/Codecs/ObjCodecs.package").GroupID; + } + + [Test] + public void TestSuccessfullyLoadsObjectModule() + { + var objectModuleAsset = ContentProvider.Get() + .GetAsset(new ResourceKey(0x1, _groupID, TypeIDs.OBJM)); + + Assert.That(objectModuleAsset, Is.Not.Null); + } +} \ No newline at end of file diff --git a/Assets/Tests/OpenTS2/Files/Formats/DBPF/ObjectModuleCodecTest.cs.meta b/Assets/Tests/OpenTS2/Files/Formats/DBPF/ObjectModuleCodecTest.cs.meta new file mode 100644 index 00000000..3236872a --- /dev/null +++ b/Assets/Tests/OpenTS2/Files/Formats/DBPF/ObjectModuleCodecTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c290fd6845f2442c94c96b4a69ddddc3 +timeCreated: 1727972116 \ No newline at end of file diff --git a/Assets/Tests/OpenTS2/Files/Formats/DBPF/SimsObjectCodecTest.cs b/Assets/Tests/OpenTS2/Files/Formats/DBPF/SimsObjectCodecTest.cs new file mode 100644 index 00000000..f26c8ca1 --- /dev/null +++ b/Assets/Tests/OpenTS2/Files/Formats/DBPF/SimsObjectCodecTest.cs @@ -0,0 +1,26 @@ +using System.Linq; +using NUnit.Framework; +using OpenTS2.Common; +using OpenTS2.Content; +using OpenTS2.Content.DBPF; +using OpenTS2.Files.Formats.DBPF; + +public class SimsObjectCodecTest +{ + private uint _groupID; + + [SetUp] + public void SetUp() + { + TestMain.Initialize(); + _groupID = ContentProvider.Get().AddPackage("TestAssets/Codecs/ObjCodecs.package").GroupID; + } + + [Test] + public void TestSuccessfullyLoadsSimsObject() + { + var objectAsset = ContentProvider.Get().GetAsset(new ResourceKey(0x158, _groupID, TypeIDs.XOBJ)); + + Assert.That(objectAsset, Is.Not.Null); + } +} \ No newline at end of file diff --git a/Assets/Tests/OpenTS2/Files/Formats/DBPF/SimsObjectCodecTest.cs.meta b/Assets/Tests/OpenTS2/Files/Formats/DBPF/SimsObjectCodecTest.cs.meta new file mode 100644 index 00000000..08115342 --- /dev/null +++ b/Assets/Tests/OpenTS2/Files/Formats/DBPF/SimsObjectCodecTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 13359c34be754888864a20da2042ea57 +timeCreated: 1727122336 \ No newline at end of file