diff --git a/Volatility/CLI/Commands/AutotestCommand.cs b/Volatility/CLI/Commands/AutotestCommand.cs index b253441..8182809 100644 --- a/Volatility/CLI/Commands/AutotestCommand.cs +++ b/Volatility/CLI/Commands/AutotestCommand.cs @@ -145,7 +145,7 @@ public void TestHeaderRW(string name, TextureBase header, bool skipImport = fals Console.ResetColor(); } - using (EndianAwareBinaryWriter writer = new(fs, header.GetResourceEndian())) + using (ResourceBinaryWriter writer = new(fs, header.ResourceEndian)) { Console.WriteLine($"AUTOTEST - Writing autotest {name} to working directory..."); header.WriteToStream(writer); diff --git a/Volatility/Operations/Resources/CreateShaderProgramBufferOperation.cs b/Volatility/Operations/Resources/CreateShaderProgramBufferOperation.cs index 05b528e..a68f1b8 100644 --- a/Volatility/Operations/Resources/CreateShaderProgramBufferOperation.cs +++ b/Volatility/Operations/Resources/CreateShaderProgramBufferOperation.cs @@ -42,7 +42,7 @@ public void WriteToFile(ShaderProgramBufferBase buffer, string outputPath, Platf ValidatePlatform(platform); - Platform bufferPlatform = buffer.GetResourcePlatform(); + Platform bufferPlatform = buffer.ResourcePlatform; if (bufferPlatform != Platform.Agnostic && bufferPlatform != platform) throw new InvalidOperationException($"ShaderProgramBuffer platform mismatch: buffer={bufferPlatform}, requested={platform}."); @@ -53,11 +53,11 @@ public void WriteToFile(ShaderProgramBufferBase buffer, string outputPath, Platf } using FileStream fs = new(outputPath, FileMode.Create); - Endian endian = buffer.GetResourceEndian(); + Endian endian = buffer.ResourceEndian; if (endian == Endian.Agnostic) endian = EndianMapping.GetDefaultEndian(platform); - using EndianAwareBinaryWriter writer = new(fs, endian); + using ResourceBinaryWriter writer = new(fs, endian); buffer.WriteToStream(writer, endian); } diff --git a/Volatility/Operations/Resources/ExportResourceOperation.cs b/Volatility/Operations/Resources/ExportResourceOperation.cs index 8dbd2fe..4d2df09 100644 --- a/Volatility/Operations/Resources/ExportResourceOperation.cs +++ b/Volatility/Operations/Resources/ExportResourceOperation.cs @@ -16,11 +16,11 @@ public Task ExecuteAsync(Resource resource, string outputPath, Platform platform using FileStream fs = new(outputPath, FileMode.Create); - Endian endian = resource.GetResourceEndian() != Endian.Agnostic - ? resource.GetResourceEndian() + Endian endian = resource.ResourceEndian != Endian.Agnostic + ? resource.ResourceEndian : EndianMapping.GetDefaultEndian(platform); - using EndianAwareBinaryWriter writer = new(fs, endian); + using ResourceBinaryWriter writer = new(fs, endian); switch (resource) { diff --git a/Volatility/Operations/Resources/PortTextureOperation.cs b/Volatility/Operations/Resources/PortTextureOperation.cs index 5e1b8e9..6096cc7 100644 --- a/Volatility/Operations/Resources/PortTextureOperation.cs +++ b/Volatility/Operations/Resources/PortTextureOperation.cs @@ -208,12 +208,12 @@ public async Task ExecuteAsync(IEnumerable sourceFiles, string sourceFor try { - if (useGtf && sourceTexture.GetResourcePlatform() == Platform.PS3) + if (useGtf && sourceTexture.ResourcePlatform == Platform.PS3) { PS3TextureUtilities.PS3GTFToDDS(sourcePath, sourceBitmapPath, destinationBitmapPath, verbose); } - if (destinationTexture is TextureX360 destX && sourceTexture.GetResourcePlatform() != Platform.X360) + if (destinationTexture is TextureX360 destX && sourceTexture.ResourcePlatform != Platform.X360) { destX.Format.MaxMipLevel = destX.Format.MinMipLevel; } @@ -271,7 +271,7 @@ public async Task ExecuteAsync(IEnumerable sourceFiles, string sourceFor } using FileStream fs = new(outPath, FileMode.Create, FileAccess.Write); - using (EndianAwareBinaryWriter writer = new(fs, destinationTexture.GetResourceEndian())) + using (ResourceBinaryWriter writer = new(fs, destinationTexture.ResourceEndian)) { try { @@ -300,7 +300,7 @@ public async Task ExecuteAsync(IEnumerable sourceFiles, string sourceFor private string BPRx64Hack(TextureBase header, string format) { - if (header.GetResourcePlatform() == Platform.BPR && format.EndsWith("X64", StringComparison.Ordinal)) + if (header.ResourcePlatform == Platform.BPR && format.EndsWith("X64", StringComparison.Ordinal)) { header.SetResourceArch(Arch.x64); return "BPR"; diff --git a/Volatility/Resources/AptData/AptData.cs b/Volatility/Resources/AptData/AptData.cs index db4e4e0..90cabb8 100644 --- a/Volatility/Resources/AptData/AptData.cs +++ b/Volatility/Resources/AptData/AptData.cs @@ -4,14 +4,14 @@ namespace Volatility.Resources; public class AptData : Resource { - public override ResourceType GetResourceType() => ResourceType.AptData; - public override Platform GetResourcePlatform() => Platform.Agnostic; + public override ResourceType ResourceType => ResourceType.AptData; + public override Platform ResourcePlatform => Platform.Agnostic; public string MovieName; public string BaseComponentName; public GuiGeometryObject GuiGeometry; - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { base.WriteToStream(writer); } diff --git a/Volatility/Resources/BinaryResource.cs b/Volatility/Resources/BinaryResource.cs index f0034cc..4cf68b6 100644 --- a/Volatility/Resources/BinaryResource.cs +++ b/Volatility/Resources/BinaryResource.cs @@ -9,7 +9,7 @@ namespace Volatility.Resources; public class BinaryResource : Resource { - public override ResourceType GetResourceType() => ResourceType.BinaryFile; + public override ResourceType ResourceType => ResourceType.BinaryFile; public uint DataSize { get; set; } public uint DataOffset { get; set; } @@ -34,7 +34,7 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann reader.BaseStream.Seek(DataOffset, SeekOrigin.Begin); } - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { writer.Write(DataSize); writer.Write(DataOffset); diff --git a/Volatility/Resources/EnvironmentKeyframe/EnvironmentKeyframe.cs b/Volatility/Resources/EnvironmentKeyframe/EnvironmentKeyframe.cs index c103515..1b8bf42 100644 --- a/Volatility/Resources/EnvironmentKeyframe/EnvironmentKeyframe.cs +++ b/Volatility/Resources/EnvironmentKeyframe/EnvironmentKeyframe.cs @@ -9,7 +9,7 @@ namespace Volatility.Resources; public class EnvironmentKeyframe : Resource { - public override ResourceType GetResourceType() => ResourceType.EnvironmentKeyframe; + public override ResourceType ResourceType => ResourceType.EnvironmentKeyframe; public BloomData BloomSettings; public VignetteData VignetteSettings; @@ -31,86 +31,27 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann throw new Exception("Version mismatch!"); } - reader.BaseStream.Seek(0x10, SeekOrigin.Begin); - - BloomSettings = new(reader); - VignetteSettings = new(reader); - TintSettings = new(reader, GetResourceArch()); - reader.BaseStream.Seek(0xC, SeekOrigin.Current); - ScatteringSettings = new(reader); - LightingSettings = new(reader); - CloudSettings = new(reader); + reader.ParseSection(0x10, r => new BloomData(r), out BloomSettings); + reader.ParseSection(0x30, r => new VignetteData(r), out VignetteSettings); + reader.ParseSection(0x80, r => new TintData(r, ResourceArch), out TintSettings); + reader.ParseSection(0x90, r => new ScatteringData(r), out ScatteringSettings); + reader.ParseSection(0x140, r => new LightingData(r), out LightingSettings); + reader.ParseSection(0x1D0, r => new CloudsData(r), out CloudSettings); } - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { base.WriteToStream(writer, endianness); writer.Write((uint)0x8); writer.Write(new byte[0xC]); - writer.Write(BloomSettings.Luminance); - writer.Write(BloomSettings.Threshold); - writer.Write(new byte[0x8]); - writer.Write(BloomSettings.Scale); - - writer.Write(VignetteSettings.Angle); - writer.Write(VignetteSettings.Sharpness); - writer.Write(new byte[0x8]); - writer.Write(VignetteSettings.Amount, intrinsic: true); - writer.Write(VignetteSettings.Center, intrinsic: true); - writer.Write(VignetteSettings.InnerColor); - writer.Write(VignetteSettings.OuterColor); - - writer.Write((uint)0x0); // TODO: handle external ColourCube import - writer.Write(new byte[0xC]); - - writer.Write(ScatteringSettings.SkyTopColor, intrinsic: true); - writer.Write(ScatteringSettings.SkyHorizonColor, intrinsic: true); - writer.Write(ScatteringSettings.SkySunColor, intrinsic: true); - writer.Write(ScatteringSettings.SkyHorizonPower); - writer.Write(ScatteringSettings.SkySunPower); - writer.Write(ScatteringSettings.SkyDarkening); - writer.Write(ScatteringSettings.SkyHorizonBleedScale); - writer.Write(ScatteringSettings.SkyHorizonBleedPower); - writer.Write(ScatteringSettings.SkySunBleedPower); - writer.Write(new byte[0x8]); - writer.Write(ScatteringSettings.ScatteringTopColor, intrinsic: true); - writer.Write(ScatteringSettings.ScatteringHorizonColor, intrinsic: true); - writer.Write(ScatteringSettings.ScatteringSunColor, intrinsic: true); - writer.Write(ScatteringSettings.ScatteringHorizonPower); - writer.Write(ScatteringSettings.ScatteringSunPower); - writer.Write(ScatteringSettings.ScatteringDarkening); - writer.Write(ScatteringSettings.ScatteringHorizonBleedScale); - writer.Write(ScatteringSettings.ScatteringHorizonBleedPower); - writer.Write(ScatteringSettings.ScatteringSunBleedPower); - writer.Write(ScatteringSettings.ScatteringDistance, intrinsic: false); - writer.Write(ScatteringSettings.ScatteringPower); - writer.Write(ScatteringSettings.ScatteringCap); - writer.Write(new byte[0x8]); - - writer.Write(LightingSettings.KeyLightColor, intrinsic: true); - writer.Write(LightingSettings.SpecularColor, intrinsic: true); - writer.Write(LightingSettings.KeyFillColor, intrinsic: true); - writer.Write(LightingSettings.ShadowFillColor, intrinsic: true); - writer.Write(LightingSettings.RightFillColor, intrinsic: true); - writer.Write(LightingSettings.LeftFillColor, intrinsic: true); - writer.Write(LightingSettings.UpFillColor, intrinsic: true); - writer.Write(LightingSettings.DownFillColor, intrinsic: true); - writer.Write(LightingSettings.AmbientIrradianceScale); - writer.Write(new byte[0xC]); - - writer.Write(CloudSettings.Layer1LiteColor, intrinsic: true); - writer.Write(CloudSettings.Layer2LiteColor, intrinsic: true); - writer.Write(CloudSettings.Layer1DarkColor, intrinsic: true); - writer.Write(CloudSettings.Layer2DarkColor, intrinsic: true); - writer.Write(CloudSettings.LayerDensity, intrinsic: false); - writer.Write(CloudSettings.LayerFeathering, intrinsic: false); - writer.Write(CloudSettings.LayerOpacity, intrinsic: false); - writer.Write(CloudSettings.LayerSpeed, intrinsic: false); - writer.Write(CloudSettings.LayerScale, intrinsic: false); - writer.Write(CloudSettings.DirectionAngle); - writer.Write(new byte[0x4]); + writer.WriteSection(writer.BaseStream.Position, BloomSettings, BloomData.Write); + writer.WriteSection(writer.BaseStream.Position, VignetteSettings, VignetteData.Write); + writer.WriteSection(writer.BaseStream.Position, TintSettings, TintData.Write); + writer.WriteSection(writer.BaseStream.Position, ScatteringSettings, ScatteringData.Write); + writer.WriteSection(writer.BaseStream.Position, LightingSettings, LightingData.Write); + writer.WriteSection(writer.BaseStream.Position, CloudSettings, CloudsData.Write); } } @@ -132,6 +73,14 @@ public BloomData(ResourceBinaryReader reader) reader.BaseStream.Seek(0x8, SeekOrigin.Current); Scale = reader.ReadVector4(); } + + public static void Write(ResourceBinaryWriter writer, BloomData value) + { + writer.Write(value.Luminance); + writer.Write(value.Threshold); + writer.Write(new byte[0x8]); + writer.Write(value.Scale); + } } public struct VignetteData @@ -153,6 +102,17 @@ public VignetteData(ResourceBinaryReader reader) InnerColor = reader.ReadColorRGBA(); OuterColor = reader.ReadColorRGBA(); } + + public static void Write(ResourceBinaryWriter writer, VignetteData value) + { + writer.Write(value.Angle); + writer.Write(value.Sharpness); + writer.Write(new byte[0x8]); + writer.Write(value.Amount, intrinsic: true); + writer.Write(value.Center, intrinsic: true); + writer.Write(value.InnerColor); + writer.Write(value.OuterColor); + } } public struct TintData @@ -165,6 +125,12 @@ public TintData(ResourceBinaryReader reader, Arch arch) if (ResourceImport.ReadExternalImport(0, reader, 0x240, out ResourceImport ExternalReference)) ColorCubeReference = ExternalReference; } + + public static void Write(ResourceBinaryWriter writer, TintData value) + { + writer.Write((uint)0x0); // TODO: handle external ColourCube import + writer.Write(new byte[0xC]); + } } public struct ScatteringData @@ -217,6 +183,33 @@ public ScatteringData(ResourceBinaryReader reader) ScatteringCap = reader.ReadSingle(); reader.BaseStream.Seek(0x8, SeekOrigin.Current); } + + public static void Write(ResourceBinaryWriter writer, ScatteringData value) + { + writer.Write(value.SkyTopColor, intrinsic: true); + writer.Write(value.SkyHorizonColor, intrinsic: true); + writer.Write(value.SkySunColor, intrinsic: true); + writer.Write(value.SkyHorizonPower); + writer.Write(value.SkySunPower); + writer.Write(value.SkyDarkening); + writer.Write(value.SkyHorizonBleedScale); + writer.Write(value.SkyHorizonBleedPower); + writer.Write(value.SkySunBleedPower); + writer.Write(new byte[0x8]); + writer.Write(value.ScatteringTopColor, intrinsic: true); + writer.Write(value.ScatteringHorizonColor, intrinsic: true); + writer.Write(value.ScatteringSunColor, intrinsic: true); + writer.Write(value.ScatteringHorizonPower); + writer.Write(value.ScatteringSunPower); + writer.Write(value.ScatteringDarkening); + writer.Write(value.ScatteringHorizonBleedScale); + writer.Write(value.ScatteringHorizonBleedPower); + writer.Write(value.ScatteringSunBleedPower); + writer.Write(value.ScatteringDistance, intrinsic: false); + writer.Write(value.ScatteringPower); + writer.Write(value.ScatteringCap); + writer.Write(new byte[0x8]); + } } public struct LightingData @@ -244,6 +237,19 @@ public LightingData(ResourceBinaryReader reader) AmbientIrradianceScale = reader.ReadSingle(); reader.BaseStream.Seek(0xC, SeekOrigin.Current); } + public static void Write(ResourceBinaryWriter writer, LightingData value) + { + writer.Write(value.KeyLightColor, intrinsic: true); + writer.Write(value.SpecularColor, intrinsic: true); + writer.Write(value.KeyFillColor, intrinsic: true); + writer.Write(value.ShadowFillColor, intrinsic: true); + writer.Write(value.RightFillColor, intrinsic: true); + writer.Write(value.LeftFillColor, intrinsic: true); + writer.Write(value.UpFillColor, intrinsic: true); + writer.Write(value.DownFillColor, intrinsic: true); + writer.Write(value.AmbientIrradianceScale); + writer.Write(new byte[0xC]); + } } public struct CloudsData @@ -273,4 +279,18 @@ public CloudsData(ResourceBinaryReader reader) DirectionAngle = reader.ReadSingle(); reader.BaseStream.Seek(0x4, SeekOrigin.Current); } + public static void Write(ResourceBinaryWriter writer, CloudsData value) + { + writer.Write(value.Layer1LiteColor, intrinsic: true); + writer.Write(value.Layer2LiteColor, intrinsic: true); + writer.Write(value.Layer1DarkColor, intrinsic: true); + writer.Write(value.Layer2DarkColor, intrinsic: true); + writer.Write(value.LayerDensity, intrinsic: false); + writer.Write(value.LayerFeathering, intrinsic: false); + writer.Write(value.LayerOpacity, intrinsic: false); + writer.Write(value.LayerSpeed, intrinsic: false); + writer.Write(value.LayerScale, intrinsic: false); + writer.Write(value.DirectionAngle); + writer.Write(new byte[0x4]); + } } \ No newline at end of file diff --git a/Volatility/Resources/EnvironmentTimeLine/EnvironmentTimeLine.cs b/Volatility/Resources/EnvironmentTimeLine/EnvironmentTimeLine.cs index a072f49..a7539e9 100644 --- a/Volatility/Resources/EnvironmentTimeLine/EnvironmentTimeLine.cs +++ b/Volatility/Resources/EnvironmentTimeLine/EnvironmentTimeLine.cs @@ -4,11 +4,11 @@ namespace Volatility.Resources; public class EnvironmentTimeline : Resource { - public override ResourceType GetResourceType() => ResourceType.EnvironmentTimeLine; + public override ResourceType ResourceType => ResourceType.EnvironmentTimeLine; public LocationData[] Locations; - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { base.WriteToStream(writer, endianness); @@ -22,7 +22,7 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann { base.ParseFromStream(reader, endianness); - Arch arch = GetResourceArch(); + Arch arch = ResourceArch; int version = reader.ReadInt32(); if (version != 1) @@ -38,9 +38,10 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann for (int i = 0; i < locationCount; i++) { reader.BaseStream.Seek(locationsPtr + ((arch == Arch.x64 ? 0x18 : 0xC) * i), SeekOrigin.Begin); + // This is the same kind of 32/64 count value that StreamedDeformationSpec uses - may move that boilerplate and change this? uint keyframeCount = (uint)(arch == Arch.x64 ? reader.ReadUInt64() : reader.ReadUInt32()); - ulong keyframeTimesPtr = (arch == Arch.x64 ? reader.ReadUInt64() : reader.ReadUInt32()); - ulong keyframeRefsPtr = (arch == Arch.x64 ? reader.ReadUInt64() : reader.ReadUInt32()); + ulong keyframeTimesPtr = reader.ReadPointer(ResourceArch); + ulong keyframeRefsPtr = reader.ReadPointer(ResourceArch); Locations[i].Keyframes = new KeyframeReference[keyframeCount]; diff --git a/Volatility/Resources/GuiPopup/GuiPopup.cs b/Volatility/Resources/GuiPopup/GuiPopup.cs index 0e970ac..2c9e886 100644 --- a/Volatility/Resources/GuiPopup/GuiPopup.cs +++ b/Volatility/Resources/GuiPopup/GuiPopup.cs @@ -8,10 +8,10 @@ public class GuiPopup : Resource const int PopupStructSize = 0xC0; - public override ResourceType GetResourceType() => ResourceType.GuiPopup; - public override Platform GetResourcePlatform() => Platform.Agnostic; + public override ResourceType ResourceType => ResourceType.GuiPopup; + public override Platform ResourcePlatform => Platform.Agnostic; - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness) { ushort size = (ushort)(Popups.Count * PopupStructSize); base.WriteToStream(writer, endianness); @@ -62,7 +62,7 @@ static Popup ReadOne(ResourceBinaryReader r) return p; } - static void WriteOne(EndianAwareBinaryWriter w, Popup p) + static void WriteOne(ResourceBinaryWriter w, Popup p) { w.Write(p.NameId); WriteFixedString(w, p.Name, 13); @@ -92,7 +92,7 @@ static string ReadFixedString(ResourceBinaryReader r, int len) return Encoding.ASCII.GetString(bytes); } - static void WriteFixedString(EndianAwareBinaryWriter w, string? s, int len) + static void WriteFixedString(ResourceBinaryWriter w, string? s, int len) { var bytes = Encoding.ASCII.GetBytes(s ?? string.Empty); if (bytes.Length > len) w.Write(bytes, 0, len); diff --git a/Volatility/Resources/InstanceList/InstanceList.cs b/Volatility/Resources/InstanceList/InstanceList.cs index 3d64772..0718d66 100644 --- a/Volatility/Resources/InstanceList/InstanceList.cs +++ b/Volatility/Resources/InstanceList/InstanceList.cs @@ -11,7 +11,7 @@ namespace Volatility.Resources; public class InstanceList : Resource { - public override ResourceType GetResourceType() => ResourceType.InstanceList; + public override ResourceType ResourceType => ResourceType.InstanceList; [EditorLabel("Number of instances"), EditorCategory("Instance List"), EditorReadOnly, EditorTooltip("The amount of instances that have a model assigned, but NOT the size of the entire instance array.")] public uint NumInstances; @@ -43,7 +43,7 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann reader.BaseStream.Seek(instanceListPtr, SeekOrigin.Begin); - long instanceBlockSize = GetResourceArch() == Arch.x64 ? 0x60 : 0x50; + long instanceBlockSize = ResourceArch == Arch.x64 ? 0x60 : 0x50; for (int i = 0; i < entries; i++) { diff --git a/Volatility/Resources/Model/Model.cs b/Volatility/Resources/Model/Model.cs index 1fe22c9..ab2af14 100644 --- a/Volatility/Resources/Model/Model.cs +++ b/Volatility/Resources/Model/Model.cs @@ -15,10 +15,10 @@ public class Model : Resource [EditorCategory("Model Container"), EditorLabel("Models")] public List ModelDatas = []; - public override ResourceType GetResourceType() => ResourceType.Model; - public override Platform GetResourcePlatform() => Platform.Agnostic; + public override ResourceType ResourceType => ResourceType.Model; + public override Platform ResourcePlatform => Platform.Agnostic; - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { base.WriteToStream(writer, endianness); @@ -123,7 +123,7 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann idRelativePtr + renderablesPtr + (numRenderables * (0x4 + 0x1 + 0x4)) + - (reader.GetEndianness() == Endian.BE ? 0x4 : 0x0), SeekOrigin.Begin + (reader.Endianness == Endian.BE ? 0x4 : 0x0), SeekOrigin.Begin ); ResourceImport.ReadExternalImport(i, reader, maxLength, out modelData.ResourceReference); diff --git a/Volatility/Resources/Renderable/RenderableBPR.cs b/Volatility/Resources/Renderable/RenderableBPR.cs index 537d6de..f41addc 100644 --- a/Volatility/Resources/Renderable/RenderableBPR.cs +++ b/Volatility/Resources/Renderable/RenderableBPR.cs @@ -2,12 +2,12 @@ public class RenderableBPR : RenderableBase { - public override Endian GetResourceEndian() => Endian.LE; - public override Platform GetResourcePlatform() => Platform.BPR; + public override Endian ResourceEndian => Endian.LE; + public override Platform ResourcePlatform => Platform.BPR; public override void ParseFromStream(ResourceBinaryReader reader, Endian endianness = Endian.Agnostic) { - reader.SetEndianness(GetResourceEndian()); + reader.SetEndianness(ResourceEndian); reader.BaseStream.Seek(0x20, SeekOrigin.Begin); diff --git a/Volatility/Resources/Renderable/RenderableBase.cs b/Volatility/Resources/Renderable/RenderableBase.cs index 6faa271..43ef5be 100644 --- a/Volatility/Resources/Renderable/RenderableBase.cs +++ b/Volatility/Resources/Renderable/RenderableBase.cs @@ -21,7 +21,7 @@ public abstract class RenderableBase : Resource public uint IndexBuffer; // Only on PC platforms public uint VertexBuffer; // Only on PC platforms - public override ResourceType GetResourceType() => ResourceType.Renderable; + public override ResourceType ResourceType => ResourceType.Renderable; public override void ParseFromStream(ResourceBinaryReader reader, Endian endianness = Endian.Agnostic) { diff --git a/Volatility/Resources/Renderable/RenderablePC.cs b/Volatility/Resources/Renderable/RenderablePC.cs index 174209f..5ef382a 100644 --- a/Volatility/Resources/Renderable/RenderablePC.cs +++ b/Volatility/Resources/Renderable/RenderablePC.cs @@ -2,12 +2,12 @@ public class RenderablePC : RenderableBase { - public override Endian GetResourceEndian() => Endian.LE; - public override Platform GetResourcePlatform() => Platform.TUB; + public override Endian ResourceEndian => Endian.LE; + public override Platform ResourcePlatform => Platform.TUB; public override void ParseFromStream(ResourceBinaryReader reader, Endian endianness = Endian.Agnostic) { - reader.SetEndianness(GetResourceEndian()); + reader.SetEndianness(ResourceEndian); reader.BaseStream.Seek(0x20, SeekOrigin.Begin); diff --git a/Volatility/Resources/Renderable/RenderablePS3.cs b/Volatility/Resources/Renderable/RenderablePS3.cs index 950253a..575295c 100644 --- a/Volatility/Resources/Renderable/RenderablePS3.cs +++ b/Volatility/Resources/Renderable/RenderablePS3.cs @@ -2,8 +2,8 @@ public class RenderablePS3 : RenderableBase { - public override Endian GetResourceEndian() => Endian.BE; - public override Platform GetResourcePlatform() => Platform.PS3; + public override Endian ResourceEndian => Endian.BE; + public override Platform ResourcePlatform => Platform.PS3; public RenderablePS3(string path, Endian endianness = Endian.Agnostic) : base(path, endianness) { } } diff --git a/Volatility/Resources/Renderable/RenderableX360.cs b/Volatility/Resources/Renderable/RenderableX360.cs index ad74476..3335b2d 100644 --- a/Volatility/Resources/Renderable/RenderableX360.cs +++ b/Volatility/Resources/Renderable/RenderableX360.cs @@ -2,8 +2,8 @@ public class RenderableX360 : RenderableBase { - public override Endian GetResourceEndian() => Endian.BE; - public override Platform GetResourcePlatform() => Platform.X360; + public override Endian ResourceEndian => Endian.BE; + public override Platform ResourcePlatform => Platform.X360; public RenderableX360(string path, Endian endianness = Endian.Agnostic) : base(path, endianness) { } } diff --git a/Volatility/Resources/Resource.cs b/Volatility/Resources/Resource.cs index 1186c97..5cf1c01 100644 --- a/Volatility/Resources/Resource.cs +++ b/Volatility/Resources/Resource.cs @@ -19,24 +19,24 @@ public abstract class Resource [EditorCategory("Import Data"), EditorLabel("Unpacker"), EditorTooltip("The tool used to extract this resource from a bundle.")] public Unpacker Unpacker = Unpacker.Raw; - public virtual ResourceType GetResourceType() => ResourceType.Invalid; - public virtual Endian GetResourceEndian() => Endian.Agnostic; // Forced endianness for platform-specific resources (e.g. Textures) - public virtual Platform GetResourcePlatform() => Platform.Agnostic; - public virtual Arch GetResourceArch() => Arch; + public virtual ResourceType ResourceType => ResourceType.Invalid; + public virtual Endian ResourceEndian => Endian.Agnostic; // Forced endianness for platform-specific resources (e.g. Textures) + public virtual Platform ResourcePlatform => Platform.Agnostic; + public virtual Arch ResourceArch => Arch; public virtual void SetResourceArch(Arch newArch) { Arch = newArch; } - public virtual void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public virtual void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { - if (GetResourceEndian() != Endian.Agnostic) - writer.SetEndianness(GetResourceEndian()); + if (ResourceEndian != Endian.Agnostic) + writer.SetEndianness(ResourceEndian); else if (endianness != Endian.Agnostic) writer.SetEndianness(endianness); } public virtual void ParseFromStream(ResourceBinaryReader reader, Endian endianness = Endian.Agnostic) { - if (GetResourceEndian() != Endian.Agnostic) - reader.SetEndianness(GetResourceEndian()); + if (ResourceEndian != Endian.Agnostic) + reader.SetEndianness(ResourceEndian); else if (endianness != Endian.Agnostic) reader.SetEndianness(endianness); @@ -57,7 +57,7 @@ public Resource(string path, Endian endianness = Endian.Agnostic) string? name = Path.GetFileNameWithoutExtension(ImportedFileName); - Endian importEndianness = (GetResourceEndian() != Endian.Agnostic) ? GetResourceEndian() : endianness; + Endian importEndianness = (ResourceEndian != Endian.Agnostic) ? ResourceEndian : endianness; if (!string.IsNullOrEmpty(name)) { diff --git a/Volatility/Resources/ResourceFactory.cs b/Volatility/Resources/ResourceFactory.cs index 35a7a02..ba346d4 100644 --- a/Volatility/Resources/ResourceFactory.cs +++ b/Volatility/Resources/ResourceFactory.cs @@ -69,6 +69,12 @@ public static class ResourceFactory { (ResourceType.SnapshotData, Platform.X360), path => new SnapshotData(path, Endian.BE) }, { (ResourceType.SnapshotData, Platform.PS3), path => new SnapshotData(path, Endian.BE) }, + // StreamedDeformationSpec resources + { (ResourceType.StreamedDeformationSpec, Platform.BPR), path => new StreamedDeformationSpec(path, Endian.LE) }, + { (ResourceType.StreamedDeformationSpec, Platform.TUB), path => new StreamedDeformationSpec(path, Endian.LE) }, + { (ResourceType.StreamedDeformationSpec, Platform.X360), path => new StreamedDeformationSpec(path, Endian.BE) }, + { (ResourceType.StreamedDeformationSpec, Platform.PS3), path => new StreamedDeformationSpec(path, Endian.BE) }, + // AptData resources { (ResourceType.AptData, Platform.BPR), path => new AptData(path, Endian.LE) }, { (ResourceType.AptData, Platform.TUB), path => new AptData(path, Endian.LE) }, diff --git a/Volatility/Resources/Shader/ShaderBase.cs b/Volatility/Resources/Shader/ShaderBase.cs index 190f475..e07dedd 100644 --- a/Volatility/Resources/Shader/ShaderBase.cs +++ b/Volatility/Resources/Shader/ShaderBase.cs @@ -2,9 +2,9 @@ public class ShaderBase : Resource { - public override ResourceType GetResourceType() => ResourceType.Shader; - public override Endian GetResourceEndian() => Endian.Agnostic; - public override Platform GetResourcePlatform() => Platform.Agnostic; + public override ResourceType ResourceType => ResourceType.Shader; + public override Endian ResourceEndian => Endian.Agnostic; + public override Platform ResourcePlatform => Platform.Agnostic; [EditorCategory("Shader/Source"), EditorLabel("Source File"), EditorTooltip("Relative path to the HLSL source file.")] public string? ShaderSourcePath { get; set; } @@ -29,7 +29,7 @@ public class ShaderBase : Resource [EditorCategory("Shader/Compile"), EditorLabel("Additional Arguments"), EditorTooltip("Extra dxc command-line arguments.")] public List AdditionalArguments { get; set; } = []; - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness) { base.WriteToStream(writer, endianness); } diff --git a/Volatility/Resources/Shader/ShaderPC.cs b/Volatility/Resources/Shader/ShaderPC.cs index 79f575c..a7b485d 100644 --- a/Volatility/Resources/Shader/ShaderPC.cs +++ b/Volatility/Resources/Shader/ShaderPC.cs @@ -7,14 +7,14 @@ namespace Volatility.Resources; public class ShaderPC : ShaderBase { - public override Endian GetResourceEndian() => Endian.LE; - public override Platform GetResourcePlatform() => Platform.TUB; + public override Endian ResourceEndian => Endian.LE; + public override Platform ResourcePlatform => Platform.TUB; public string Name; private static readonly Regex DbToFileRegex = new(@"(\?ID=\d+)|:", RegexOptions.Compiled); - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness) { base.WriteToStream(writer, endianness); } diff --git a/Volatility/Resources/ShaderProgramBuffer/ShaderProgramBufferBPR.cs b/Volatility/Resources/ShaderProgramBuffer/ShaderProgramBufferBPR.cs index cce1fe9..eb1faff 100644 --- a/Volatility/Resources/ShaderProgramBuffer/ShaderProgramBufferBPR.cs +++ b/Volatility/Resources/ShaderProgramBuffer/ShaderProgramBufferBPR.cs @@ -8,8 +8,8 @@ namespace Volatility.Resources; public class ShaderProgramBufferBPR : ShaderProgramBufferBase { - public override Endian GetResourceEndian() => Endian.LE; - public override Platform GetResourcePlatform() => Platform.BPR; + public override Endian ResourceEndian => Endian.LE; + public override Platform ResourcePlatform => Platform.BPR; public ShaderType CompiledShaderType; @@ -21,7 +21,7 @@ public class ShaderProgramBufferBPR : ShaderProgramBufferBase public List Globals = []; - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { base.WriteToStream(writer, endianness); diff --git a/Volatility/Resources/ShaderProgramBuffer/ShaderProgramBufferBase.cs b/Volatility/Resources/ShaderProgramBuffer/ShaderProgramBufferBase.cs index ccb6fcc..3c3dd8a 100644 --- a/Volatility/Resources/ShaderProgramBuffer/ShaderProgramBufferBase.cs +++ b/Volatility/Resources/ShaderProgramBuffer/ShaderProgramBufferBase.cs @@ -2,11 +2,11 @@ public class ShaderProgramBufferBase : Resource { - public override ResourceType GetResourceType() => ResourceType.RwShaderProgramBuffer; - public override Endian GetResourceEndian() => Endian.Agnostic; - public override Platform GetResourcePlatform() => Platform.Agnostic; + public override ResourceType ResourceType => ResourceType.RwShaderProgramBuffer; + public override Endian ResourceEndian => Endian.Agnostic; + public override Platform ResourcePlatform => Platform.Agnostic; - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness) { base.WriteToStream(writer, endianness); } diff --git a/Volatility/Resources/SnapshotData/SnapshotData.cs b/Volatility/Resources/SnapshotData/SnapshotData.cs index 2314b44..5880532 100644 --- a/Volatility/Resources/SnapshotData/SnapshotData.cs +++ b/Volatility/Resources/SnapshotData/SnapshotData.cs @@ -4,13 +4,13 @@ namespace Volatility.Resources; public class SnapshotData : BinaryResource { - public override ResourceType GetResourceType() => ResourceType.SnapshotData; - public override Platform GetResourcePlatform() => Platform.Agnostic; + public override ResourceType ResourceType => ResourceType.SnapshotData; + public override Platform ResourcePlatform => Platform.Agnostic; public List Channels = []; public List SnapshotStatuses = []; - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { DataSize = (uint)(0x10 + (Channels.Count * 0xC) + (SnapshotStatuses.Count * 0x8)); diff --git a/Volatility/Resources/Splicer/Splicer.cs b/Volatility/Resources/Splicer/Splicer.cs index af1257c..e452605 100644 --- a/Volatility/Resources/Splicer/Splicer.cs +++ b/Volatility/Resources/Splicer/Splicer.cs @@ -13,8 +13,8 @@ namespace Volatility.Resources; public class Splicer : BinaryResource { - public override ResourceType GetResourceType() => ResourceType.Splicer; - + public override ResourceType ResourceType => ResourceType.Splicer; + public List Splices = []; // Only gets populated when parsing from a stream, or when @@ -134,7 +134,7 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann } } - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { LoadDependentSamples(); diff --git a/Volatility/Resources/StreamedDeformationSpec/StreamedDeformationSpec.cs b/Volatility/Resources/StreamedDeformationSpec/StreamedDeformationSpec.cs new file mode 100644 index 0000000..ee475cb --- /dev/null +++ b/Volatility/Resources/StreamedDeformationSpec/StreamedDeformationSpec.cs @@ -0,0 +1,754 @@ +using System.Numerics; +using Volatility.Utilities; + +namespace Volatility.Resources; + +// The StreamedDeformationSpec resource type defines per-vehicle +// deformation data such as tag points, IK body parts, wheels, sensors, +// locator tags, and glass panes. +// +// Learn More: +// https://burnout.wiki/wiki/Streamed_Deformation + +public struct WheelSpec +{ + public const int Size = 0x30; + + public Vector3 Position; + public Vector3 Scale; + public int TagPointIndex; + + public WheelSpec(ResourceBinaryReader reader) + { + Position = reader.ReadVector3(); + Scale = reader.ReadVector3(); + TagPointIndex = reader.ReadInt32(); + reader.BaseStream.Seek(0xC, SeekOrigin.Current); + } +} + +public struct SensorSpec +{ + public const int Size = 0x40; + + public Vector3 InitialOffset; + public float[] DirectionParams; + public float Radius; + public byte[] NextSensor; + public byte SceneIndex; + public byte AbsorbtionLevel; + public byte[] NextBoundarySensor; + + public SensorSpec(ResourceBinaryReader reader) + { + InitialOffset = reader.ReadVector3(); + + DirectionParams = new float[6]; + for (int i = 0; i < DirectionParams.Length; i++) + { + DirectionParams[i] = reader.ReadSingle(); + } + + Radius = reader.ReadSingle(); + NextSensor = reader.ReadBytes(6); + SceneIndex = reader.ReadByte(); + AbsorbtionLevel = reader.ReadByte(); + NextBoundarySensor = reader.ReadBytes(2); + reader.BaseStream.Seek(0xA, SeekOrigin.Current); + } +} + +public struct TagPointSpec +{ + public const int Size = 0x50; + + public Vector3Plus OffsetFromAAndWeightA; + public Vector3Plus OffsetFromBAndWeightB; + public Vector3Plus InitialPositionAndDetachThreshold; + public float WeightA; + public float WeightB; + public float DetachThresholdSquared; + public short DeformationSensorA; + public short DeformationSensorB; + public sbyte JointIndex; + public bool SkinnedPoint; + + public TagPointSpec(ResourceBinaryReader reader) + { + OffsetFromAAndWeightA = reader.ReadVector3Plus(); + OffsetFromBAndWeightB = reader.ReadVector3Plus(); + InitialPositionAndDetachThreshold = reader.ReadVector3Plus(); + WeightA = reader.ReadSingle(); + WeightB = reader.ReadSingle(); + DetachThresholdSquared = reader.ReadSingle(); + DeformationSensorA = reader.ReadInt16(); + DeformationSensorB = reader.ReadInt16(); + JointIndex = reader.ReadSByte(); + SkinnedPoint = reader.ReadBoolean(); + reader.BaseStream.Seek(0xE, SeekOrigin.Current); + } +} + +public struct IKDrivenPointSpec +{ + public const int Size = 0x20; + + public Vector3 InitialPos; + public float DistanceFromA; + public float DistanceFromB; + public short TagPointIndexA; + public short TagPointIndexB; + + public IKDrivenPointSpec(ResourceBinaryReader reader) + { + InitialPos = reader.ReadVector3(); + DistanceFromA = reader.ReadSingle(); + DistanceFromB = reader.ReadSingle(); + TagPointIndexA = reader.ReadInt16(); + TagPointIndexB = reader.ReadInt16(); + reader.BaseStream.Seek(0x4, SeekOrigin.Current); + } +} + +public struct LocatorPointSpec +{ + public const int Size = 0x50; + + public Matrix4x4 LocatorMatrix; + public int TagPointType; + public short IkPartIndex; + public byte SkinPoint; + + public LocatorPointSpec(ResourceBinaryReader reader) + { + LocatorMatrix = MatrixUtilities.ReadMatrix44(reader); + TagPointType = reader.ReadInt32(); + IkPartIndex = reader.ReadInt16(); + SkinPoint = reader.ReadByte(); + reader.BaseStream.Seek(0x9, SeekOrigin.Current); + } +} + +public struct LocatorPointSpecList +{ + public uint Count; + public ulong Offset; + + public LocatorPointSpecList(ResourceBinaryReader reader, Arch arch) + { + Count = reader.ReadUInt32(); + if (arch == Arch.x64) + { + reader.BaseStream.Seek(0x4, SeekOrigin.Current); + Offset = reader.ReadUInt64(); + } + else + { + Offset = reader.ReadUInt32(); + } + } +} + +public struct BBoxPointSkinData +{ + public const int Size = 0x20; + + public Vector3 Vertex; + public float[] Weights; + public byte[] BoneIndices; + + public BBoxPointSkinData(ResourceBinaryReader reader) + { + Vertex = reader.ReadVector3(); + Weights = + [ + reader.ReadSingle(), + reader.ReadSingle(), + reader.ReadSingle() + ]; + BoneIndices = reader.ReadBytes(3); + reader.BaseStream.Seek(0x1, SeekOrigin.Current); + } +} + +public struct BodyPartBBoxSpec +{ + public const int Size = 0x180; + + public Matrix4x4 Orientation; + public List CornerSkinData; + public BBoxPointSkinData CentreSkinData; + public BBoxPointSkinData JointSkinData; + + public BodyPartBBoxSpec(ResourceBinaryReader reader) + { + Orientation = MatrixUtilities.ReadMatrix44(reader); + + CornerSkinData = new List(8); + for (int i = 0; i < 8; i++) + { + CornerSkinData.Add(new BBoxPointSkinData(reader)); + } + + CentreSkinData = new BBoxPointSkinData(reader); + JointSkinData = new BBoxPointSkinData(reader); + } +} + +public struct DeformationJointSpec +{ + public const int Size = 0x40; + + public Vector3 JointPosition; + public Vector3 JointAxis; + public Vector3 JointDefaultDirection; + public float MaxJointAngle; + public float JointDetachThreshold; + public int JointType; + + public DeformationJointSpec(ResourceBinaryReader reader) + { + JointPosition = reader.ReadVector3(); + JointAxis = reader.ReadVector3(); + JointDefaultDirection = reader.ReadVector3(); + MaxJointAngle = reader.ReadSingle(); + JointDetachThreshold = reader.ReadSingle(); + JointType = reader.ReadInt32(); + reader.BaseStream.Seek(0x4, SeekOrigin.Current); + } +} + +public struct IKBodyPartSpec +{ + public static int GetSize(Arch arch) => arch == Arch.x64 ? 0x1E4 : 0x1E0; + + public Matrix4x4 GraphicsTransform; + public BodyPartBBoxSpec BBoxSkinData; + public ulong JointSpecsOffset; + public int NumJoints; + public int PartGraphics; + public int StartIndexOfDrivenPoints; + public int NumberOfDrivenPoints; + public int StartIndexOfTagPoints; + public int NumberOfTagPoints; + public int PartType; + public List JointSpecs; + + public IKBodyPartSpec(ResourceBinaryReader reader, Arch arch) + { + long structStart = reader.BaseStream.Position; + + GraphicsTransform = MatrixUtilities.ReadMatrix44(reader); + BBoxSkinData = new BodyPartBBoxSpec(reader); + JointSpecsOffset = reader.ReadPointer(arch); + NumJoints = reader.ReadInt32(); + PartGraphics = reader.ReadInt32(); + StartIndexOfDrivenPoints = reader.ReadInt32(); + NumberOfDrivenPoints = reader.ReadInt32(); + StartIndexOfTagPoints = reader.ReadInt32(); + NumberOfTagPoints = reader.ReadInt32(); + PartType = reader.ReadInt32(); + + JointSpecs = new List(Math.Max(NumJoints, 0)); + + long structEnd = structStart + GetSize(arch); + long originalPosition = reader.BaseStream.Position; + + if (NumJoints > 0 && JointSpecsOffset != 0) + { + reader.BaseStream.Seek((long)JointSpecsOffset, SeekOrigin.Begin); + for (int i = 0; i < NumJoints; i++) + { + JointSpecs.Add(new DeformationJointSpec(reader)); + } + } + + reader.BaseStream.Seek(Math.Max(structEnd, originalPosition), SeekOrigin.Begin); + } +} + +public struct GlassPaneSpec +{ + public const int Size = 0x70; + + public Vector3 Normal; + public Vector3[] CornerPositionOffsets; + public short[] PointIndices; + public bool[] SkinToControlPoint; + public short ParentBodyPart; + public short CrackSensor; + public short SmashSensor; + public int PartType; + + public GlassPaneSpec(ResourceBinaryReader reader) + { + Normal = reader.ReadVector3(); + + CornerPositionOffsets = new Vector3[4]; + for (int i = 0; i < CornerPositionOffsets.Length; i++) + { + CornerPositionOffsets[i] = reader.ReadVector3(); + } + + PointIndices = new short[4]; + for (int i = 0; i < PointIndices.Length; i++) + { + PointIndices[i] = reader.ReadInt16(); + } + + SkinToControlPoint = new bool[4]; + for (int i = 0; i < SkinToControlPoint.Length; i++) + { + SkinToControlPoint[i] = reader.ReadByte() != 0; + } + + ParentBodyPart = reader.ReadInt16(); + CrackSensor = reader.ReadInt16(); + SmashSensor = reader.ReadInt16(); + reader.BaseStream.Seek(0x2, SeekOrigin.Current); + PartType = reader.ReadInt32(); + reader.BaseStream.Seek(0x8, SeekOrigin.Current); + } +} + +public class StreamedDeformationSpec : Resource +{ + public const int HeaderSize32 = 0x6B0; + public const int HeaderSize64 = 0x6F0; + private const int SectionAlignment = 0x10; + + public override ResourceType ResourceType => ResourceType.StreamedDeformationSpec; + + public int VersionNumber { get; set; } + public ulong TagPointDataOffset { get; set; } + public int NumberOfTagPoints { get; set; } + public ulong DrivenPointDataOffset { get; set; } + public int NumberOfDrivenPoints { get; set; } + public ulong IKPartDataOffset { get; set; } + public int NumberOfIKParts { get; set; } + public ulong GlassPaneDataOffset { get; set; } + public int NumGlassPanes { get; set; } + public LocatorPointSpecList GenericTagsInfo { get; set; } + public LocatorPointSpecList CameraTagsInfo { get; set; } + public LocatorPointSpecList LightTagsInfo { get; set; } + public Vector3 HandlingBodyDimensions { get; set; } + public List WheelSpecs { get; set; } = new(); + public List DeformationSensorSpecs { get; set; } = new(); + public Matrix4x4 CarModelSpaceToHandlingBodySpaceTransform { get; set; } + public byte SpecID { get; set; } + public byte NumVehicleBodies { get; set; } + public byte NumDeformationSensors { get; set; } + public byte NumGraphicsParts { get; set; } + public Vector3 CurrentCOMOffset { get; set; } + public Vector3 MeshOffset { get; set; } + public Vector3 RigidBodyOffset { get; set; } + public Vector3 CollisionOffset { get; set; } + public Vector3 InertiaTensor { get; set; } + public List TagPointSpecs { get; set; } = new(); + public List DrivenPoints { get; set; } = new(); + public List GenericTags { get; set; } = new(); + public List CameraTags { get; set; } = new(); + public List LightTags { get; set; } = new(); + public List IKParts { get; set; } = new(); + public List GlassPanes { get; set; } = new(); + + public override void ParseFromStream(ResourceBinaryReader reader, Endian endianness = Endian.Agnostic) + { + base.ParseFromStream(reader, endianness); + + WheelSpecs.Clear(); + DeformationSensorSpecs.Clear(); + TagPointSpecs.Clear(); + DrivenPoints.Clear(); + GenericTags.Clear(); + CameraTags.Clear(); + LightTags.Clear(); + IKParts.Clear(); + GlassPanes.Clear(); + + Arch arch = ResourceArch; + + VersionNumber = reader.ReadInt32(); + if (VersionNumber != 1) + { + throw new InvalidDataException($"Version mismatch! Version should be 1. (Found version {VersionNumber})"); + } + + if (arch == Arch.x64) + { + reader.BaseStream.Seek(0x4, SeekOrigin.Current); + } + + TagPointDataOffset = reader.ReadPointer(arch); + NumberOfTagPoints = ReadCount(arch); + DrivenPointDataOffset = reader.ReadPointer(arch); + NumberOfDrivenPoints = ReadCount(reader, arch); + IKPartDataOffset = reader.ReadPointer(arch); + NumberOfIKParts = ReadCount(reader, arch); + GlassPaneDataOffset = reader.ReadPointer(arch); + NumGlassPanes = ReadCount(reader, arch); + GenericTagsInfo = new LocatorPointSpecList(reader, arch); + CameraTagsInfo = new LocatorPointSpecList(reader, arch); + LightTagsInfo = new LocatorPointSpecList(reader, arch); + + reader.BaseStream.Seek(arch == Arch.x64 ? 0x8 : 0x4, SeekOrigin.Current); + + HandlingBodyDimensions = reader.ReadVector3(); + + for (int i = 0; i < 4; i++) + { + WheelSpecs.Add(new WheelSpec(reader)); + } + + for (int i = 0; i < 20; i++) + { + DeformationSensorSpecs.Add(new SensorSpec(reader)); + } + + CarModelSpaceToHandlingBodySpaceTransform = MatrixUtilities.ReadMatrix44(reader); + + SpecID = reader.ReadByte(); + NumVehicleBodies = reader.ReadByte(); + NumDeformationSensors = reader.ReadByte(); + NumGraphicsParts = reader.ReadByte(); + reader.BaseStream.Seek(0xC, SeekOrigin.Current); + + CurrentCOMOffset = reader.ReadVector3(); + MeshOffset = reader.ReadVector3(); + RigidBodyOffset = reader.ReadVector3(); + CollisionOffset = reader.ReadVector3(); + InertiaTensor = reader.ReadVector3(); + + reader.ParseSection(TagPointDataOffset, NumberOfTagPoints, r => new TagPointSpec(r), TagPointSpecs); + reader.ParseSection(DrivenPointDataOffset, NumberOfDrivenPoints, r => new IKDrivenPointSpec(r), DrivenPoints); + reader.ParseSection(GenericTagsInfo.Offset, (int)GenericTagsInfo.Count, r => new LocatorPointSpec(r), GenericTags); + reader.ParseSection(CameraTagsInfo.Offset, (int)CameraTagsInfo.Count, r => new LocatorPointSpec(r), CameraTags); + reader.ParseSection(LightTagsInfo.Offset, (int)LightTagsInfo.Count, r => new LocatorPointSpec(r), LightTags); + reader.ParseSection(GlassPaneDataOffset, NumGlassPanes, r => new GlassPaneSpec(r), GlassPanes); + reader.ParseSection(IKPartDataOffset, NumberOfIKParts, r => new IKBodyPartSpec(r, arch), IKParts); + } + + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) + { + base.WriteToStream(writer, endianness); + + Arch arch = ResourceArch; + int headerSize = arch == Arch.x64 ? HeaderSize64 : HeaderSize32; + int ikPartStructSize = IKBodyPartSpec.GetSize(arch); + + if (VersionNumber == 0) + { + VersionNumber = 1; + } + + if (VersionNumber != 1) + { + throw new InvalidDataException($"Version mismatch! Version should be 1. (Found version {VersionNumber})"); + } + + if (WheelSpecs.Count > 4) + { + throw new InvalidDataException($"StreamedDeformationSpec only supports 4 wheel specs. Found {WheelSpecs.Count}."); + } + + if (DeformationSensorSpecs.Count > 20) + { + throw new InvalidDataException($"StreamedDeformationSpec only supports 20 sensor specs. Found {DeformationSensorSpecs.Count}."); + } + + List wheelSpecs = ResourceUtilities.GetFixedSizeList(WheelSpecs, 4); + List sensorSpecs = ResourceUtilities.GetFixedSizeList(DeformationSensorSpecs, 20); + + NumberOfTagPoints = TagPointSpecs.Count; + NumberOfDrivenPoints = DrivenPoints.Count; + NumberOfIKParts = IKParts.Count; + NumGlassPanes = GlassPanes.Count; + + long currentOffset = headerSize; + + TagPointDataOffset = ResourceUtilities.GetSectionOffset(ref currentOffset, NumberOfTagPoints, TagPointSpec.Size, SectionAlignment); + DrivenPointDataOffset = ResourceUtilities.GetSectionOffset(ref currentOffset, NumberOfDrivenPoints, IKDrivenPointSpec.Size, SectionAlignment); + IKPartDataOffset = ResourceUtilities.GetSectionOffset(ref currentOffset, NumberOfIKParts, ikPartStructSize, SectionAlignment); + + ulong[] jointSpecOffsets = new ulong[IKParts.Count]; + for (int i = 0; i < IKParts.Count; i++) + { + int jointCount = IKParts[i].JointSpecs?.Count ?? 0; + if (jointCount > 0) + { + currentOffset = ResourceUtilities.AlignOffset(currentOffset, SectionAlignment); + jointSpecOffsets[i] = (ulong)currentOffset; + currentOffset += jointCount * DeformationJointSpec.Size; + } + } + + currentOffset = ResourceUtilities.AlignOffset(currentOffset, SectionAlignment); + + GlassPaneDataOffset = ResourceUtilities.GetSectionOffset(ref currentOffset, NumGlassPanes, GlassPaneSpec.Size, SectionAlignment); + + GenericTagsInfo = new LocatorPointSpecList + { + Count = (uint)GenericTags.Count, + Offset = ResourceUtilities.GetSectionOffset(ref currentOffset, GenericTags.Count, LocatorPointSpec.Size, SectionAlignment) + }; + CameraTagsInfo = new LocatorPointSpecList + { + Count = (uint)CameraTags.Count, + Offset = ResourceUtilities.GetSectionOffset(ref currentOffset, CameraTags.Count, LocatorPointSpec.Size, SectionAlignment) + }; + LightTagsInfo = new LocatorPointSpecList + { + Count = (uint)LightTags.Count, + Offset = ResourceUtilities.GetSectionOffset(ref currentOffset, LightTags.Count, LocatorPointSpec.Size, SectionAlignment) + }; + + writer.Write(VersionNumber); + if (arch == Arch.x64) + { + writer.Write(new byte[0x4]); + } + + writer.WritePointer(TagPointDataOffset, arch); + WriteCount(writer, NumberOfTagPoints, arch); + writer.WritePointer(DrivenPointDataOffset, arch); + WriteCount(writer, NumberOfDrivenPoints, arch); + writer.WritePointer(IKPartDataOffset, arch); + WriteCount(writer, NumberOfIKParts, arch); + writer.WritePointer(GlassPaneDataOffset, arch); + WriteCount(writer, NumGlassPanes, arch); + WriteLocatorPointSpecList(writer, GenericTagsInfo, arch); + WriteLocatorPointSpecList(writer, CameraTagsInfo, arch); + WriteLocatorPointSpecList(writer, LightTagsInfo, arch); + + writer.WritePointer(0, arch); + writer.Write(HandlingBodyDimensions, intrinsic: true); + + for (int i = 0; i < wheelSpecs.Count; i++) + { + WriteWheelSpec(writer, wheelSpecs[i]); + } + + for (int i = 0; i < sensorSpecs.Count; i++) + { + WriteSensorSpec(writer, sensorSpecs[i]); + } + + MatrixUtilities.WriteMatrix44(writer, CarModelSpaceToHandlingBodySpaceTransform); + + writer.Write(SpecID); + writer.Write(NumVehicleBodies); + writer.Write(NumDeformationSensors); + writer.Write(NumGraphicsParts); + writer.Write(new byte[0xC]); + + writer.Write(CurrentCOMOffset, intrinsic: true); + writer.Write(MeshOffset, intrinsic: true); + writer.Write(RigidBodyOffset, intrinsic: true); + writer.Write(CollisionOffset, intrinsic: true); + writer.Write(InertiaTensor, intrinsic: true); + + if (writer.BaseStream.Position != headerSize) + { + throw new InvalidDataException($"Header size mismatch while writing StreamedDeformationSpec. Expected 0x{headerSize:X}, wrote 0x{writer.BaseStream.Position:X}."); + } + + writer.WriteSection(TagPointDataOffset, TagPointSpecs, WriteTagPointSpec); + writer.WriteSection(DrivenPointDataOffset, DrivenPoints, WriteIKDrivenPointSpec); + writer.WriteSection(IKPartDataOffset, IKParts, (w, part, index) => WriteIKBodyPartSpec(w, part, arch, jointSpecOffsets[index])); + + for (int i = 0; i < IKParts.Count; i++) + { + if (jointSpecOffsets[i] == 0) + { + continue; + } + + writer.BaseStream.Position = (long)jointSpecOffsets[i]; + List joints = IKParts[i].JointSpecs ?? []; + for (int j = 0; j < joints.Count; j++) + { + WriteDeformationJointSpec(writer, joints[j]); + } + } + + writer.WriteSection(GlassPaneDataOffset, GlassPanes, WriteGlassPaneSpec); + writer.WriteSection(GenericTagsInfo.Offset, GenericTags, WriteLocatorPointSpec); + writer.WriteSection(CameraTagsInfo.Offset, CameraTags, WriteLocatorPointSpec); + writer.WriteSection(LightTagsInfo.Offset, LightTags, WriteLocatorPointSpec); + } + + public StreamedDeformationSpec() : base() { } + + public StreamedDeformationSpec(string path, Endian endianness = Endian.Agnostic) : base(path, endianness) { } + + // Honestly not sure if this count formatting is used anywhere else - move it to generic boilerplate if so? + private static void WriteArchCount(ResourceBinaryWriter writer, int count, Arch arch) + { + writer.Write(count); + if (arch == Arch.x64) + { + writer.Write(0x00000000); + } + } + + // Section writers + + private static void WriteLocatorPointSpecList(ResourceBinaryWriter writer, LocatorPointSpecList value, Arch arch) + { + writer.Write(value.Count); + if (arch == Arch.x64) + { + writer.Write(0); + writer.Write(value.Offset); + } + else + { + if (value.Offset > uint.MaxValue) + { + throw new InvalidDataException($"Locator pointer 0x{value.Offset:X} does not fit in a 32-bit StreamedDeformationSpec."); + } + + writer.Write((uint)value.Offset); + } + } + + private static void WriteWheelSpec(ResourceBinaryWriter writer, WheelSpec value) + { + writer.Write(value.Position, intrinsic: true); + writer.Write(value.Scale, intrinsic: true); + writer.Write(value.TagPointIndex); + writer.Write(new byte[0xC]); + } + + private static void WriteSensorSpec(ResourceBinaryWriter writer, SensorSpec value) + { + writer.Write(value.InitialOffset, intrinsic: true); + + for (int i = 0; i < 6; i++) + { + writer.Write(i < value.DirectionParams?.Length ? value.DirectionParams[i] : 0f); + } + + writer.Write(value.Radius); + writer.WriteFixedBytes(value.NextSensor, 6); + writer.Write(value.SceneIndex); + writer.Write(value.AbsorbtionLevel); + writer.WriteFixedBytes(value.NextBoundarySensor, 2); + writer.Write(new byte[0xA]); + } + + private static void WriteTagPointSpec(ResourceBinaryWriter writer, TagPointSpec value) + { + writer.Write(value.OffsetFromAAndWeightA); + writer.Write(value.OffsetFromBAndWeightB); + writer.Write(value.InitialPositionAndDetachThreshold); + writer.Write(value.WeightA); + writer.Write(value.WeightB); + writer.Write(value.DetachThresholdSquared); + writer.Write(value.DeformationSensorA); + writer.Write(value.DeformationSensorB); + writer.Write(value.JointIndex); + writer.Write(value.SkinnedPoint); + writer.Write(new byte[0xE]); + } + + private static void WriteIKDrivenPointSpec(ResourceBinaryWriter writer, IKDrivenPointSpec value) + { + writer.Write(value.InitialPos, intrinsic: true); + writer.Write(value.DistanceFromA); + writer.Write(value.DistanceFromB); + writer.Write(value.TagPointIndexA); + writer.Write(value.TagPointIndexB); + writer.Write(new byte[0x4]); + } + + private static void WriteLocatorPointSpec(ResourceBinaryWriter writer, LocatorPointSpec value) + { + MatrixUtilities.WriteMatrix44(writer, value.LocatorMatrix); + writer.Write(value.TagPointType); + writer.Write(value.IkPartIndex); + writer.Write(value.SkinPoint); + writer.Write(new byte[0x9]); + } + + private static void WriteIKBodyPartSpec(ResourceBinaryWriter writer, IKBodyPartSpec value, Arch arch, ulong jointSpecsOffset) + { + MatrixUtilities.WriteMatrix44(writer, value.GraphicsTransform); + WriteBodyPartBBoxSpec(writer, value.BBoxSkinData); + writer.WritePointer(jointSpecsOffset, arch); + writer.Write(value.JointSpecs?.Count ?? 0); + writer.Write(value.PartGraphics); + writer.Write(value.StartIndexOfDrivenPoints); + writer.Write(value.NumberOfDrivenPoints); + writer.Write(value.StartIndexOfTagPoints); + writer.Write(value.NumberOfTagPoints); + writer.Write(value.PartType); + } + + private static void WriteBodyPartBBoxSpec(ResourceBinaryWriter writer, BodyPartBBoxSpec value) + { + MatrixUtilities.WriteMatrix44(writer, value.Orientation); + + for (int i = 0; i < 8; i++) + { + WriteBBoxPointSkinData( + writer, + i < value.CornerSkinData?.Count ? value.CornerSkinData[i] : default + ); + } + + WriteBBoxPointSkinData(writer, value.CentreSkinData); + WriteBBoxPointSkinData(writer, value.JointSkinData); + } + + private static void WriteBBoxPointSkinData(ResourceBinaryWriter writer, BBoxPointSkinData value) + { + writer.Write(value.Vertex, intrinsic: true); + + for (int i = 0; i < 3; i++) + { + writer.Write(i < value.Weights?.Length ? value.Weights[i] : 0f); + } + + writer.WriteFixedBytes(value.BoneIndices, 3); + writer.Write((byte)0); + } + + private static void WriteDeformationJointSpec(ResourceBinaryWriter writer, DeformationJointSpec value) + { + writer.Write(value.JointPosition, intrinsic: true); + writer.Write(value.JointAxis, intrinsic: true); + writer.Write(value.JointDefaultDirection, intrinsic: true); + writer.Write(value.MaxJointAngle); + writer.Write(value.JointDetachThreshold); + writer.Write(value.JointType); + writer.Write(new byte[0x4]); + } + + private static void WriteGlassPaneSpec(ResourceBinaryWriter writer, GlassPaneSpec value) + { + writer.Write(value.Normal, intrinsic: true); + + for (int i = 0; i < 4; i++) + { + writer.Write(i < value.CornerPositionOffsets?.Length ? value.CornerPositionOffsets[i] : default, intrinsic: true); + } + + for (int i = 0; i < 4; i++) + { + writer.Write(i < value.PointIndices?.Length ? value.PointIndices[i] : (short)0); + } + + for (int i = 0; i < 4; i++) + { + writer.Write((byte)((i < value.SkinToControlPoint?.Length && value.SkinToControlPoint[i]) ? 1 : 0)); + } + + writer.Write(value.ParentBodyPart); + writer.Write(value.CrackSensor); + writer.Write(value.SmashSensor); + writer.Write(new byte[0x2]); + writer.Write(value.PartType); + writer.Write(new byte[0x8]); + } +} diff --git a/Volatility/Resources/Texture/TextureBPR.cs b/Volatility/Resources/Texture/TextureBPR.cs index a5c818b..3bf1a80 100644 --- a/Volatility/Resources/Texture/TextureBPR.cs +++ b/Volatility/Resources/Texture/TextureBPR.cs @@ -4,8 +4,8 @@ namespace Volatility.Resources; public class TextureBPR : TextureBase { - public override Endian GetResourceEndian() => Endian.LE; - public override Platform GetResourcePlatform() => Platform.BPR; + public override Endian ResourceEndian => Endian.LE; + public override Platform ResourcePlatform => Platform.BPR; public D3D11_USAGE Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT; // Usually default, implemented for parity sake @@ -38,17 +38,17 @@ public override void PushInternalFormat() { } public override void PullInternalFormat() { } public override void PushInternalFlags() { } - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { base.WriteToStream(writer, endianness); - writer.Write(x64Switch(GetResourceArch() == Arch.x64, 0)); // TextureInterfacePtr, 64 bit + writer.WritePointer(0, ResourceArch); // TextureInterfacePtr, 64 bit writer.Write((uint)Usage); writer.Write((uint)Dimension); - writer.Write(x64Switch(GetResourceArch() == Arch.x64, 0)); // PixelDataPtr, 64 bit - writer.Write(x64Switch(GetResourceArch() == Arch.x64, 0)); // ShaderResourceViewInterface0Ptr, 64 bit - writer.Write(x64Switch(GetResourceArch() == Arch.x64, 0)); // ShaderResourceViewInterface1Ptr, 64 bit - writer.Write((uint)0); // Unknown0 + writer.WritePointer(0, ResourceArch); // PixelDataPtr, 64 bit + writer.WritePointer(0, ResourceArch); // ShaderResourceViewInterface0Ptr, 64 bit + writer.WritePointer(0, ResourceArch); // ShaderResourceViewInterface1Ptr, 64 bit + writer.Write((uint)0); // Unknown0 writer.Write((uint)Format); writer.Write((uint)Flags); writer.Write(Width); @@ -57,13 +57,13 @@ public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endian writer.Write(ArraySize); writer.Write(MostDetailedMip); writer.Write(MipmapLevels); - writer.Write((ushort)0); // Unknown1 - writer.Write(x64Switch(GetResourceArch() == Arch.x64, 0)); // Unknown2, 64 bit + writer.Write((ushort)0); // Unknown1 + writer.WritePointer(0, ResourceArch); // Unknown2, 64 bit writer.Write((int)PlacedTileMode); writer.Write(PlacedDataSize); - writer.Write(x64Switch(GetResourceArch() == Arch.x64, 0)); // TextureData, 64 bit + writer.WritePointer(0, ResourceArch); // TextureData, 64 bit - if (GetResourceArch() == Arch.x64) + if (ResourceArch == Arch.x64) { writer.Write("Volatili"u8.ToArray()); } @@ -75,10 +75,10 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann SetResourceArch(reader.BaseStream.Length > 0x40 ? Arch.x64 : Arch.x32); - reader.BaseStream.Seek(GetResourceArch() == Arch.x64 ? 0x8 : 0x4, SeekOrigin.Begin); // Skip TextureInterfacePtr + reader.BaseStream.Seek(ResourceArch == Arch.x64 ? 0x8 : 0x4, SeekOrigin.Begin); // Skip TextureInterfacePtr Usage = (D3D11_USAGE)reader.ReadInt32(); Dimension = (DIMENSION)reader.ReadInt32(); - reader.BaseStream.Seek(GetResourceArch() == Arch.x64 ? 0x18 : 0xC, SeekOrigin.Current); // Skip pointers + reader.BaseStream.Seek(ResourceArch == Arch.x64 ? 0x18 : 0xC, SeekOrigin.Current); // Skip pointers reader.BaseStream.Seek(0x4, SeekOrigin.Current); // Skip Unknown0 Format = (DXGI_FORMAT)reader.ReadInt32(); Flags = (BPRTextureFlags)reader.ReadUInt32(); @@ -89,10 +89,10 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann MostDetailedMip = reader.ReadByte(); MipmapLevels = reader.ReadByte(); reader.BaseStream.Seek(sizeof(ushort), SeekOrigin.Current); // Skip Unknown1 - reader.BaseStream.Seek(GetResourceArch() == Arch.x64 ? 0x8 : 0x4, SeekOrigin.Current); // Unknown 2, 64 bit + reader.BaseStream.Seek(ResourceArch == Arch.x64 ? 0x8 : 0x4, SeekOrigin.Current); // Unknown 2, 64 bit PlacedTileMode = (XG_TILE_MODE)reader.ReadInt32(); PlacedDataSize = (uint)reader.ReadInt32(); - reader.BaseStream.Seek(GetResourceArch() == Arch.x64 ? 0x8 : 0x4, SeekOrigin.Current); // TextureData, 64 bit + reader.BaseStream.Seek(ResourceArch == Arch.x64 ? 0x8 : 0x4, SeekOrigin.Current); // TextureData, 64 bit } public override void PushInternalDimension() diff --git a/Volatility/Resources/Texture/TextureBase.cs b/Volatility/Resources/Texture/TextureBase.cs index 1d34b11..3eaa152 100644 --- a/Volatility/Resources/Texture/TextureBase.cs +++ b/Volatility/Resources/Texture/TextureBase.cs @@ -10,7 +10,7 @@ public abstract class TextureBase : Resource { - public override ResourceType GetResourceType() => ResourceType.Texture; + public override ResourceType ResourceType => ResourceType.Texture; [EditorCategory("Texture"), EditorLabel("Width"), EditorTooltip("The target width of the texture.")] public ushort Width { get; set; } diff --git a/Volatility/Resources/Texture/TexturePC.cs b/Volatility/Resources/Texture/TexturePC.cs index 43279d1..c9df887 100644 --- a/Volatility/Resources/Texture/TexturePC.cs +++ b/Volatility/Resources/Texture/TexturePC.cs @@ -4,8 +4,8 @@ namespace Volatility.Resources; public class TexturePC : TextureBase { - public override Endian GetResourceEndian() => Endian.LE; - public override Platform GetResourcePlatform() => Platform.TUB; + public override Endian ResourceEndian => Endian.LE; + public override Platform ResourcePlatform => Platform.TUB; private D3DFORMAT _format = D3DFORMAT.D3DFMT_UNKNOWN; public D3DFORMAT Format @@ -32,14 +32,14 @@ public TexturePC() : base() { } public TexturePC(string path, Endian endianness = Endian.Agnostic) : base(path, endianness) { } - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { base.WriteToStream(writer, endianness); PushAll(); // Need to determine if should be moved - writer.Write(TextureDataPtr.ToInt32()); - writer.Write(TextureInterfacePtr.ToInt32()); + writer.WritePointer((ulong)TextureDataPtr, ResourceArch); + writer.WritePointer((ulong)TextureInterfacePtr, ResourceArch); writer.Write(Unknown0); // Unknown writer.Write(MemoryClass); writer.Write(Unknown1); // Unknown diff --git a/Volatility/Resources/Texture/TexturePS3.cs b/Volatility/Resources/Texture/TexturePS3.cs index 8ba9bf2..80c65f2 100644 --- a/Volatility/Resources/Texture/TexturePS3.cs +++ b/Volatility/Resources/Texture/TexturePS3.cs @@ -4,8 +4,8 @@ namespace Volatility.Resources; public class TexturePS3 : TextureBase { - public override Endian GetResourceEndian() => Endian.BE; - public override Platform GetResourcePlatform() => Platform.PS3; + public override Endian ResourceEndian => Endian.BE; + public override Platform ResourcePlatform => Platform.PS3; public CELL_GCM_COLOR_FORMAT Format; public CELL_GCM_TEXTURE_DIMENSION CellDimension; @@ -74,7 +74,7 @@ public override void PushInternalFlags() public override void PushInternalFormat() { /* TODO But don't throw an error! */ } - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { base.WriteToStream(writer, endianness); diff --git a/Volatility/Resources/Texture/TextureX360.cs b/Volatility/Resources/Texture/TextureX360.cs index 6637b15..b11bb11 100644 --- a/Volatility/Resources/Texture/TextureX360.cs +++ b/Volatility/Resources/Texture/TextureX360.cs @@ -10,8 +10,8 @@ namespace Volatility.Resources; public class TextureX360 : TextureBase { - public override Endian GetResourceEndian() => Endian.BE; - public override Platform GetResourcePlatform() => Platform.X360; + public override Endian ResourceEndian => Endian.BE; + public override Platform ResourcePlatform => Platform.X360; // TODO: Replace this bit array with something better public BitArray D3DResourceFlags = new BitArray(28); @@ -131,7 +131,7 @@ public override void PushInternalFormat() Format.MipAddress = CalculateMipAddressX360(Width, Height); } - public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endianness = Endian.Agnostic) + public override void WriteToStream(ResourceBinaryWriter writer, Endian endianness = Endian.Agnostic) { base.WriteToStream(writer, endianness); diff --git a/Volatility/Types.cs b/Volatility/Types.cs index 081e342..7f40b1b 100644 --- a/Volatility/Types.cs +++ b/Volatility/Types.cs @@ -4,6 +4,7 @@ global using Vector3Plus = System.Numerics.Vector4; // VectorIntrinsic global using Vector4 = System.Numerics.Vector4; global using Quaternion = System.Numerics.Quaternion; +global using Matrix44 = System.Numerics.Matrix4x4; global using Matrix44Affine = System.Numerics.Matrix4x4; // Volatilty Types diff --git a/Volatility/Utilities/DataUtilities.cs b/Volatility/Utilities/DataUtilities.cs index 8f0bbc4..6c3d7cc 100644 --- a/Volatility/Utilities/DataUtilities.cs +++ b/Volatility/Utilities/DataUtilities.cs @@ -10,11 +10,6 @@ public static byte TrimIntToByte(int input) return BitConverter.GetBytes(input)[0]; } - public static byte[] x64Switch(bool x64, ulong value) - { - return x64 ? BitConverter.GetBytes(value) : BitConverter.GetBytes((uint)value); - } - public static bool IsHexadecimal(string input) { foreach (char c in input) diff --git a/Volatility/Utilities/EndianAwareBinaryReader.cs b/Volatility/Utilities/EndianAwareBinaryReader.cs index 4328b76..940eb55 100644 --- a/Volatility/Utilities/EndianAwareBinaryReader.cs +++ b/Volatility/Utilities/EndianAwareBinaryReader.cs @@ -2,7 +2,7 @@ public class EndianAwareBinaryReader : BinaryReader { - private Endian _endianness; + public Endian Endianness { get; protected set; } public EndianAwareBinaryReader(Stream input, Endian endianness) : base(input) { @@ -14,59 +14,54 @@ public EndianAwareBinaryReader(Stream input, Endian endianness) : base(input) public void SetEndianness(Endian endianness) { - _endianness = endianness; - } - - public Endian GetEndianness() - { - return _endianness; + Endianness = endianness; } public override ushort ReadUInt16() { ushort value = base.ReadUInt16(); - return _endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; + return Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; } public override short ReadInt16() { short value = base.ReadInt16(); - return _endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; + return Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; } public override uint ReadUInt32() { uint value = base.ReadUInt32(); - return _endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; + return Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; } public override int ReadInt32() { int value = base.ReadInt32(); - return _endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; + return Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; } public override ulong ReadUInt64() { ulong value = base.ReadUInt64(); - return _endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; + return Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; } public override long ReadInt64() { long value = base.ReadInt64(); - return _endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; + return Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; } public override float ReadSingle() { float value = base.ReadSingle(); - return _endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; + return Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; } public override double ReadDouble() { double value = base.ReadDouble(); - return _endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; + return Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value; } } diff --git a/Volatility/Utilities/EndianAwareBinaryWriter.cs b/Volatility/Utilities/EndianAwareBinaryWriter.cs index 7b13886..7a060fc 100644 --- a/Volatility/Utilities/EndianAwareBinaryWriter.cs +++ b/Volatility/Utilities/EndianAwareBinaryWriter.cs @@ -2,7 +2,7 @@ public class EndianAwareBinaryWriter : BinaryWriter { - private Endian _endianness; + public Endian Endianness { get; protected set; } public EndianAwareBinaryWriter(Stream output, Endian endianness) : base(output) { @@ -14,73 +14,46 @@ public EndianAwareBinaryWriter(Stream output, Endian endianness) : base(output) public void SetEndianness(Endian endianness) { - _endianness = endianness; + Endianness = endianness; } - public Endian GetEndianness() - { - return _endianness; - } public override void Write(ushort value) { - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); } public override void Write(short value) { - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); } public override void Write(uint value) { - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); } public override void Write(int value) { - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); } public override void Write(ulong value) { - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); } public override void Write(long value) { - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); } public override void Write(float value) { - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); } public override void Write(double value) { - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); - } - - public void Write(Vector2 value, bool intrinsic = false) - { - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value.X) : value.X); - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value.Y) : value.Y); - if (intrinsic) base.Write(new byte[0x8]); - } - - public void Write(Vector3 value, bool intrinsic = false) - { - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value.X) : value.X); - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value.Y) : value.Y); - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value.Z) : value.Z); - if (intrinsic) base.Write(new byte[0x4]); - } - - public void Write(Vector4 value) - { - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value.X) : value.X); - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value.Y) : value.Y); - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value.Z) : value.Z); - base.Write(_endianness == Endian.BE ? EndianUtilities.SwapEndian(value.W) : value.W); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value) : value); } } diff --git a/Volatility/Utilities/MatrixUtilities.cs b/Volatility/Utilities/MatrixUtilities.cs index ffcde8c..a69907e 100644 --- a/Volatility/Utilities/MatrixUtilities.cs +++ b/Volatility/Utilities/MatrixUtilities.cs @@ -1,4 +1,6 @@ -namespace Volatility.Utilities; +using System.Numerics; + +namespace Volatility.Utilities; public static class MatrixUtilities { @@ -34,7 +36,7 @@ public static Transform Matrix44AffineToTransform(Matrix44Affine matrix) return transform; } - public static Matrix44Affine ReadMatrix44Affine(BinaryReader reader) + public static Matrix44 ReadMatrix44(BinaryReader reader) { float m11 = reader.ReadSingle(); float m12 = reader.ReadSingle(); @@ -53,7 +55,7 @@ public static Matrix44Affine ReadMatrix44Affine(BinaryReader reader) float m43 = reader.ReadSingle(); float m44 = reader.ReadSingle(); - return new Matrix44Affine( + return new Matrix44( m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, @@ -61,6 +63,36 @@ public static Matrix44Affine ReadMatrix44Affine(BinaryReader reader) ); } + public static Matrix44Affine ReadMatrix44Affine(BinaryReader reader) + { + return ReadMatrix44(reader); + } + + public static void WriteMatrix44(BinaryWriter writer, Matrix44 value) + { + writer.Write(value.M11); + writer.Write(value.M12); + writer.Write(value.M13); + writer.Write(value.M14); + writer.Write(value.M21); + writer.Write(value.M22); + writer.Write(value.M23); + writer.Write(value.M24); + writer.Write(value.M31); + writer.Write(value.M32); + writer.Write(value.M33); + writer.Write(value.M34); + writer.Write(value.M41); + writer.Write(value.M42); + writer.Write(value.M43); + writer.Write(value.M44); + } + + public static void WriteMatrix44Affine(BinaryWriter writer, Matrix44Affine value) + { + WriteMatrix44(writer, value); + } + public static Quaternion RotationMatrixToQuaternion( float m00, float m01, float m02, float m10, float m11, float m12, diff --git a/Volatility/Utilities/PS3TextureUtilities.cs b/Volatility/Utilities/PS3TextureUtilities.cs index b034f88..0f225c4 100644 --- a/Volatility/Utilities/PS3TextureUtilities.cs +++ b/Volatility/Utilities/PS3TextureUtilities.cs @@ -103,7 +103,7 @@ public static void PS3GTFToDDS(TexturePS3 ps3Header, string sourceBitmapPath, st { byte[] header = new byte[0xE]; using MemoryStream ps3Stream = new(header); - using EndianAwareBinaryWriter writer = new(ps3Stream, ps3Header.GetResourceEndian()); + using ResourceBinaryWriter writer = new(ps3Stream, ps3Header.ResourceEndian); ps3Header.WriteToStream(writer); ps3Stream.ReadExactly(header, 0, 0xE); diff --git a/Volatility/Utilities/PaddingUtilities.cs b/Volatility/Utilities/PaddingUtilities.cs index 3bf00eb..4f1d298 100644 --- a/Volatility/Utilities/PaddingUtilities.cs +++ b/Volatility/Utilities/PaddingUtilities.cs @@ -1,6 +1,3 @@ -using System; -using System.IO; - namespace Volatility.Utilities; public static class PaddingUtilities diff --git a/Volatility/Utilities/ResourceBinaryReader.cs b/Volatility/Utilities/ResourceBinaryReader.cs index cd1f43c..fd17099 100644 --- a/Volatility/Utilities/ResourceBinaryReader.cs +++ b/Volatility/Utilities/ResourceBinaryReader.cs @@ -1,4 +1,4 @@ -using Volatility.Utilities; +using Volatility.Resources; public class ResourceBinaryReader : EndianAwareBinaryReader { @@ -6,7 +6,7 @@ public ResourceBinaryReader(Stream input, Endian endianness) : base(input, endia public Vector2 ReadVector2() { - Vector2 value = new Vector2(base.ReadSingle(), base.ReadSingle()); + Vector2 value = new(base.ReadSingle(), base.ReadSingle()); base.BaseStream.Seek(0x8, SeekOrigin.Current); return value; } @@ -18,7 +18,7 @@ public Vector2 ReadVector2Literal() public Vector3 ReadVector3() { - Vector3 value = new Vector3(base.ReadSingle(), base.ReadSingle(), base.ReadSingle()); + Vector3 value = new(base.ReadSingle(), base.ReadSingle(), base.ReadSingle()); base.BaseStream.Seek(0x4, SeekOrigin.Current); return value; } @@ -28,6 +28,11 @@ public ColorRGB ReadColorRGB() return (ColorRGB)ReadVector3(); } + public Vector3Plus ReadVector3Plus() + { + return ReadVector4(); + } + public Vector4 ReadVector4() { return new Vector4(base.ReadSingle(), base.ReadSingle(), base.ReadSingle(), base.ReadSingle()); @@ -42,4 +47,114 @@ public ColorRGBA8 ReadColorRGBA8() { return (ColorRGBA8)ReadUInt32(); } + + internal ulong ReadArchValue(Arch arch) + { + return arch == Arch.x64 ? ReadUInt64() : ReadUInt32(); + } + + public ulong ReadPointer(Arch arch) + { + return ReadArchValue(arch); + } + + public void ParseSection(ulong offset, int count, Func parser, List destination) + { + if (count <= 0 || offset == 0) + { + return; + } + + long originalPosition = BaseStream.Position; + BaseStream.Seek((long)offset, SeekOrigin.Begin); + + for (int i = 0; i < count; i++) + { + destination.Add(parser(this)); + } + + BaseStream.Seek(originalPosition, SeekOrigin.Begin); + } + + public void ParseSection(long offset, int count, Func parser, List destination) + { + if (count <= 0 || offset == 0) + { + return; + } + + long originalPosition = BaseStream.Position; + BaseStream.Seek(offset, SeekOrigin.Begin); + + for (int i = 0; i < count; i++) + { + destination.Add(parser(this)); + } + + BaseStream.Seek(originalPosition, SeekOrigin.Begin); + } + + public void ParseSection(ulong offset, Func parser, out T destination) + { + if (offset == 0) + { + destination = default!; + return; + } + + long originalPosition = BaseStream.Position; + BaseStream.Seek((long)offset, SeekOrigin.Begin); + + try + { + destination = parser(this); + } + finally + { + BaseStream.Seek(originalPosition, SeekOrigin.Begin); + } + } + + public void ParseSection(long offset, Func parser, out T destination) + { + if (offset == 0) + { + destination = default!; + return; + } + + long originalPosition = BaseStream.Position; + BaseStream.Seek(offset, SeekOrigin.Begin); + + try + { + destination = parser(this); + } + finally + { + BaseStream.Seek(originalPosition, SeekOrigin.Begin); + } + } + + public static int ReadArchDependInt(ResourceBinaryReader reader, Arch arch) + { + int value = reader.ReadInt32(); + if (arch == Arch.x64) + { + reader.BaseStream.Seek(0x4, SeekOrigin.Current); + } + + return value; + } + + public static uint ReadArchDependUInt(ResourceBinaryReader reader, Arch arch) + { + uint value = reader.ReadUInt32(); + if (arch == Arch.x64) + { + reader.BaseStream.Seek(0x4, SeekOrigin.Current); + } + + return value; + } } \ No newline at end of file diff --git a/Volatility/Utilities/ResourceBinaryWriter.cs b/Volatility/Utilities/ResourceBinaryWriter.cs new file mode 100644 index 0000000..18d507b --- /dev/null +++ b/Volatility/Utilities/ResourceBinaryWriter.cs @@ -0,0 +1,156 @@ +using Volatility.Resources; +using Volatility.Utilities; + +public class ResourceBinaryWriter : EndianAwareBinaryWriter +{ + public ResourceBinaryWriter(Stream output, Endian endianness) : base(output, endianness) { } + + public void Write(Vector2 value, bool intrinsic = false) + { + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value.X) : value.X); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value.Y) : value.Y); + if (intrinsic) base.Write(new byte[0x8]); + } + + public void Write(Vector3 value, bool intrinsic = false) + { + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value.X) : value.X); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value.Y) : value.Y); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value.Z) : value.Z); + if (intrinsic) base.Write(new byte[0x4]); + } + + public void Write(Vector4 value) + { + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value.X) : value.X); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value.Y) : value.Y); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value.Z) : value.Z); + base.Write(Endianness == Endian.BE ? EndianUtilities.SwapEndian(value.W) : value.W); + } + + public void WritePointer(ulong value, Arch arch) + { + if (arch == Arch.x64) + { + Write(value); + return; + } + + if (value > uint.MaxValue) + { + throw new InvalidDataException($"Pointer value 0x{value:X} does not fit in a 32-bit resource!"); + } + + Write((uint)value); + } + public void WriteSection(long offset, T data, Action writeItem) + { + if (offset == 0) + { + return; + } + + BaseStream.Position = offset; + writeItem(this, data); + } + + public void WriteSection(long offset, T data, Action writeItem) + { + if (offset == 0) + { + return; + } + + BaseStream.Position = offset; + writeItem(this, data, 0); + } + + public void WriteSection(long offset, List data, Action writeItem) + { + if (offset == 0 || data.Count == 0) + { + return; + } + + BaseStream.Position = offset; + for (int i = 0; i < data.Count; i++) + { + writeItem(this, data[i]); + } + } + + public void WriteSection(long offset, List data, Action writeItem) + { + if (offset == 0 || data.Count == 0) + { + return; + } + + BaseStream.Position = offset; + for (int i = 0; i < data.Count; i++) + { + writeItem(this, data[i], i); + } + } + + public void WriteSection(ulong offset, T data, Action writeItem) + { + if (offset == 0) + { + return; + } + + BaseStream.Position = (long)offset; + writeItem(this, data); + } + + public void WriteSection(ulong offset, T data, Action writeItem) + { + if (offset == 0) + { + return; + } + + BaseStream.Position = (long)offset; + writeItem(this, data, 0); + } + + public void WriteSection(ulong offset, List data, Action writeItem) + { + if (offset == 0 || data.Count == 0) + { + return; + } + + BaseStream.Position = (long)offset; + for (int i = 0; i < data.Count; i++) + { + writeItem(this, data[i]); + } + } + + public void WriteSection(ulong offset, List data, Action writeItem) + { + if (offset == 0 || data.Count == 0) + { + return; + } + + BaseStream.Position = (long)offset; + for (int i = 0; i < data.Count; i++) + { + writeItem(this, data[i], i); + } + } + + public void WriteFixedBytes(byte[]? data, int count) + { + byte[] output = new byte[count]; + if (data != null) + { + Array.Copy(data, output, Math.Min(data.Length, count)); + } + + Write(output); + } +} diff --git a/Volatility/Utilities/ResourceUtilities.cs b/Volatility/Utilities/ResourceUtilities.cs new file mode 100644 index 0000000..3c9e624 --- /dev/null +++ b/Volatility/Utilities/ResourceUtilities.cs @@ -0,0 +1,33 @@ +namespace Volatility.Utilities; + +public class ResourceUtilities +{ + public static List GetFixedSizeList(List source, int size) + { + List output = new(size); + for (int i = 0; i < size; i++) + { + output.Add(i < source.Count ? source[i] : default!); + } + + return output; + } + + public static long AlignOffset(long offset, int alignment) + { + return PaddingUtilities.GetPaddedLength(offset, alignment); + } + + public static ulong GetSectionOffset(ref long currentOffset, int count, int elementSize, int sectionAlignment) + { + if (count <= 0) + { + return 0; + } + + currentOffset = AlignOffset(currentOffset, sectionAlignment); + ulong offset = (ulong)currentOffset; + currentOffset += (long)count * elementSize; + return offset; + } +}