diff --git a/doc/game/data-center-format.md b/doc/game/data-center-format.md index 092d61e..a9f2506 100644 --- a/doc/game/data-center-format.md +++ b/doc/game/data-center-format.md @@ -83,7 +83,9 @@ struct DataCenterHeader }; ``` -`version` is currently `6`. +`version` is currently `6`. A past version `3` also existed. Note that this +field has no distinguishing value for the 32-bit and 64-bit formats, but version +`3` was only 32-bit. `timestamp` is a Unix timestamp indicating when the file was produced. @@ -94,7 +96,7 @@ data centers never include this information. `revision` indicates the version of the data graph contained within the file. It is sometimes (but not always) equal to the value sent by the client in the -`C_CHECK_VERSION` packet. +`C_CHECK_VERSION` packet. This field is not present if `version` is `3`. ### File Footer @@ -348,7 +350,7 @@ sort should be stable since the order of multiple sibling nodes with the same name can be significant for the interpretation of the data. `padding_1` and `padding_2` should be considered undefined. They were added in -the 64-bit data center format. +the 64-bit data center format, and are not present in the 32-bit format. The root node of the data graph must be located at the address `0:0`. It must have the name `__root__` and have zero attributes. @@ -436,7 +438,7 @@ type codes, the value is written directly and is accessed through the `i`, `b`, or `f` fields. `padding_1` should be considered undefined. It was added in the 64-bit data -center format. +center format, and is not present in the 32-bit format. Some nodes will have a special attribute named `__value__`. In XML terms, this represents the text of a node. For example, `bar` would be serialized diff --git a/doc/tools/dc.md b/doc/tools/dc.md index edd3065..949639f 100644 --- a/doc/tools/dc.md +++ b/doc/tools/dc.md @@ -16,11 +16,13 @@ supports the following tasks: to a fresh data center file usable by the client. * Format integrity verification of data center files, optionally with strict compliance checks. +* Support for various iterations of the data center format throughout the game's + history. In general, novadrop-dc is quite fast: It exploits as many cores as are available for parallel extraction, validation, and packing. On a modern system, -unpacking an official data center file takes around 20 seconds, while packing -the resulting data sheets takes around 40 seconds. +unpacking an official data center file takes around 15 seconds, while packing +the resulting data sheets takes around 25 seconds. ## novadrop-dc pack @@ -37,6 +39,7 @@ The `output` argument specifies the path of the resulting data center file. | Option | Description | | - | - | +| `--format ` | Specifies the data center format variant (defaults to `V6X64`). | | `--revision ` | Specifies the data tree revision number (defaults to latest known revision). | | `--compression ` | Specifies a compression level (defaults to `Optimal`). | | `--encryption-key ` | Specifies an encryption key (defaults to the latest known key). | @@ -58,7 +61,9 @@ specifies the path of the resulting data center file. | - | - | | `--decryption-key ` | Specifies a decryption key (defaults to the latest known key). | | `--decryption-iv ` | Specifies a decryption IV (defaults to the latest known IV). | +| `--architecture ` | Specifies the data center format architecture (defaults to `X64`). | | `--strict` | Enables strict format compliance checks while reading the input file. | +| `--format ` | Specifies the data center format variant (defaults to `V6X64`). | | `--revision ` | Specifies the data tree revision number (defaults to latest known revision). | | `--compression ` | Specifies a compression level (defaults to `Optimal`). | | `--encryption-key ` | Specifies an encryption key (defaults to the latest known key). | @@ -108,6 +113,7 @@ specifies the path of the directory to extract data sheets and schemas to. | - | - | | `--decryption-key ` | Specifies a decryption key (defaults to the latest known key). | | `--decryption-iv ` | Specifies a decryption IV (defaults to the latest known IV). | +| `--architecture ` | Specifies the data center format architecture (defaults to `X64`). | | `--strict` | Enables strict format compliance checks while reading the input file. | ## novadrop-dc validate @@ -138,4 +144,5 @@ The `input` argument specifies the input data center file. | - | - | | `--decryption-key ` | Specifies a decryption key (defaults to the latest known key). | | `--decryption-iv ` | Specifies a decryption IV (defaults to the latest known IV). | +| `--architecture ` | Specifies the data center format architecture (defaults to `X64`). | | `--strict` | Enables strict format compliance checks while reading the input file. | diff --git a/src/common/Buffers/SpanReader.cs b/src/common/Buffers/SpanReader.cs new file mode 100644 index 0000000..3a86e5b --- /dev/null +++ b/src/common/Buffers/SpanReader.cs @@ -0,0 +1,94 @@ +namespace Vezel.Novadrop.Buffers; + +internal ref struct SpanReader +{ + private ReadOnlySpan _remaining; + + public SpanReader(ReadOnlySpan span) + { + _remaining = span; + } + + public void Advance(int count) + { + _remaining = _remaining[count..]; + } + + public void Read(scoped Span buffer) + { + _remaining.CopyTo(buffer); + + Advance(buffer.Length); + } + + public byte ReadByte() + { + var result = _remaining[0]; + + Advance(sizeof(byte)); + + return result; + } + + public sbyte ReadSByte() + { + return (sbyte)ReadByte(); + } + + public ushort ReadUInt16() + { + var result = BinaryPrimitives.ReadUInt16LittleEndian(_remaining); + + Advance(sizeof(ushort)); + + return result; + } + + public short ReadInt16() + { + return (short)ReadUInt16(); + } + + public uint ReadUInt32() + { + var result = BinaryPrimitives.ReadUInt32LittleEndian(_remaining); + + Advance(sizeof(uint)); + + return result; + } + + public int ReadInt32() + { + return (int)ReadUInt32(); + } + + public ulong ReadUInt64() + { + var result = BinaryPrimitives.ReadUInt64LittleEndian(_remaining); + + Advance(sizeof(ulong)); + + return result; + } + + public long ReadInt64() + { + return (long)ReadUInt64(); + } + + public float ReadSingle() + { + return Unsafe.BitCast(ReadUInt32()); + } + + public double ReadDouble() + { + return Unsafe.BitCast(ReadUInt64()); + } + + public char ReadChar() + { + return (char)ReadUInt16(); + } +} diff --git a/src/common/Buffers/SpanWriter.cs b/src/common/Buffers/SpanWriter.cs new file mode 100644 index 0000000..c85ba3d --- /dev/null +++ b/src/common/Buffers/SpanWriter.cs @@ -0,0 +1,86 @@ +namespace Vezel.Novadrop.Buffers; + +internal ref struct SpanWriter +{ + private Span _remaining; + + public SpanWriter(Span span) + { + _remaining = span; + } + + public void Advance(int count) + { + _remaining = _remaining[count..]; + } + + public void Write(scoped ReadOnlySpan buffer) + { + buffer.CopyTo(_remaining); + + Advance(buffer.Length); + } + + public void WriteByte(byte value) + { + _remaining[0] = value; + + Advance(sizeof(byte)); + } + + public void WriteSByte(sbyte value) + { + WriteByte((byte)value); + } + + public void WriteUInt16(ushort value) + { + BinaryPrimitives.WriteUInt16LittleEndian(_remaining, value); + + Advance(sizeof(ushort)); + } + + public void WriteInt16(short value) + { + WriteUInt16((ushort)value); + } + + public void WriteUInt32(uint value) + { + BinaryPrimitives.WriteUInt32LittleEndian(_remaining, value); + + Advance(sizeof(uint)); + } + + public void WriteInt32(int value) + { + WriteUInt32((uint)value); + } + + public void WriteUInt64(ulong value) + { + BinaryPrimitives.WriteUInt64LittleEndian(_remaining, value); + + Advance(sizeof(ulong)); + } + + public void WriteInt64(long value) + { + WriteUInt64((ulong)value); + } + + public void WriteSingle(float value) + { + WriteUInt32(Unsafe.BitCast(value)); + } + + public void WriteDouble(double value) + { + WriteUInt64(Unsafe.BitCast(value)); + } + + public void WriteChar(char value) + { + WriteUInt16(value); + } +} diff --git a/src/common/IO/StreamBinaryReader.cs b/src/common/IO/StreamBinaryReader.cs index 1acecce..73fdba6 100644 --- a/src/common/IO/StreamBinaryReader.cs +++ b/src/common/IO/StreamBinaryReader.cs @@ -80,17 +80,13 @@ public async ValueTask ReadInt64Async(CancellationToken cancellationToken) [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] public async ValueTask ReadSingleAsync(CancellationToken cancellationToken) { - var value = await ReadUInt32Async(cancellationToken).ConfigureAwait(false); - - return Unsafe.As(ref value); + return Unsafe.BitCast(await ReadUInt32Async(cancellationToken).ConfigureAwait(false)); } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] public async ValueTask ReadDoubleAsync(CancellationToken cancellationToken) { - var value = await ReadUInt64Async(cancellationToken).ConfigureAwait(false); - - return Unsafe.As(ref value); + return Unsafe.BitCast(await ReadUInt64Async(cancellationToken).ConfigureAwait(false)); } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] diff --git a/src/common/IO/StreamBinaryWriter.cs b/src/common/IO/StreamBinaryWriter.cs index 0ca96dd..5d0ed3f 100644 --- a/src/common/IO/StreamBinaryWriter.cs +++ b/src/common/IO/StreamBinaryWriter.cs @@ -66,12 +66,12 @@ public ValueTask WriteInt64Async(long value, CancellationToken cancellationToken public ValueTask WriteSingleAsync(float value, CancellationToken cancellationToken) { - return WriteUInt32Async(Unsafe.As(ref value), cancellationToken); + return WriteUInt32Async(Unsafe.BitCast(value), cancellationToken); } public ValueTask WriteDoubleAsync(double value, CancellationToken cancellationToken) { - return WriteUInt64Async(Unsafe.As(ref value), cancellationToken); + return WriteUInt64Async(Unsafe.BitCast(value), cancellationToken); } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] diff --git a/src/formats/Data/DataCenterArchitecture.cs b/src/formats/Data/DataCenterArchitecture.cs new file mode 100644 index 0000000..5dc27fb --- /dev/null +++ b/src/formats/Data/DataCenterArchitecture.cs @@ -0,0 +1,7 @@ +namespace Vezel.Novadrop.Data; + +public enum DataCenterArchitecture +{ + X86, + X64, +} diff --git a/src/formats/Data/DataCenterFormat.cs b/src/formats/Data/DataCenterFormat.cs new file mode 100644 index 0000000..518c0b1 --- /dev/null +++ b/src/formats/Data/DataCenterFormat.cs @@ -0,0 +1,8 @@ +namespace Vezel.Novadrop.Data; + +public enum DataCenterFormat +{ + V3, + V6X86, + V6X64, +} diff --git a/src/formats/Data/DataCenterLoadOptions.cs b/src/formats/Data/DataCenterLoadOptions.cs index 960e6da..a4e9124 100644 --- a/src/formats/Data/DataCenterLoadOptions.cs +++ b/src/formats/Data/DataCenterLoadOptions.cs @@ -6,6 +6,8 @@ public sealed class DataCenterLoadOptions public ReadOnlyMemory IV { get; private set; } = DataCenter.LatestIV; + public DataCenterArchitecture Architecture { get; private set; } = DataCenterArchitecture.X64; + public bool Strict { get; private set; } public DataCenterLoaderMode Mode { get; private set; } @@ -19,6 +21,7 @@ private DataCenterLoadOptions Clone() Key = Key, IV = IV, Strict = Strict, + Architecture = Architecture, Mode = Mode, Mutability = Mutability, }; @@ -46,6 +49,17 @@ public DataCenterLoadOptions WithIV(scoped ReadOnlySpan iv) return options; } + public DataCenterLoadOptions WithArchitecture(DataCenterArchitecture architecture) + { + Check.Enum(architecture); + + var options = Clone(); + + options.Architecture = architecture; + + return options; + } + public DataCenterLoadOptions WithStrict(bool strict) { var options = Clone(); diff --git a/src/formats/Data/DataCenterSaveOptions.cs b/src/formats/Data/DataCenterSaveOptions.cs index 3a7520f..e5936ae 100644 --- a/src/formats/Data/DataCenterSaveOptions.cs +++ b/src/formats/Data/DataCenterSaveOptions.cs @@ -2,6 +2,8 @@ namespace Vezel.Novadrop.Data; public sealed class DataCenterSaveOptions { + public DataCenterFormat Format { get; private set; } = DataCenterFormat.V6X64; + public int Revision { get; private set; } = DataCenter.LatestRevision; public CompressionLevel CompressionLevel { get; private set; } @@ -18,9 +20,21 @@ private DataCenterSaveOptions Clone() CompressionLevel = CompressionLevel, Key = Key, IV = IV, + Format = Format, }; } + public DataCenterSaveOptions WithFormat(DataCenterFormat format) + { + Check.Enum(format); + + var options = Clone(); + + options.Format = format; + + return options; + } + public DataCenterSaveOptions WithRevision(int revision) { Check.Range(revision >= 0, revision); diff --git a/src/formats/Data/Serialization/DataCenterHeader.cs b/src/formats/Data/Serialization/DataCenterHeader.cs index 5bd53aa..3ae5b8f 100644 --- a/src/formats/Data/Serialization/DataCenterHeader.cs +++ b/src/formats/Data/Serialization/DataCenterHeader.cs @@ -2,8 +2,6 @@ namespace Vezel.Novadrop.Data.Serialization; internal sealed class DataCenterHeader { - private const int KnownVersion = 6; - private const double KnownTimestamp = -1.0; public int Version { get; private set; } @@ -26,29 +24,40 @@ internal sealed class DataCenterHeader public async ValueTask ReadAsync(bool strict, StreamBinaryReader reader, CancellationToken cancellationToken) { Version = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false); + + Check.Data(Version is 3 or 6, $"Unsupported data center version {Version} (expected 3 or 6)."); + Timestamp = await reader.ReadDoubleAsync(cancellationToken).ConfigureAwait(false); - Revision = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false); + + Check.Data( + Timestamp == KnownTimestamp, $"Unexpected data center timestamp {Timestamp} (expected {KnownTimestamp})."); + + if (Version == 6) + Revision = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false); + Unknown1 = await reader.ReadInt16Async(cancellationToken).ConfigureAwait(false); Unknown2 = await reader.ReadInt16Async(cancellationToken).ConfigureAwait(false); Unknown3 = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false); Unknown4 = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false); Unknown5 = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false); - Check.Data(Version == KnownVersion, $"Unsupported data center version {Version} (expected {KnownVersion})."); - Check.Data( - Timestamp == KnownTimestamp, $"Unexpected data center timestamp {Timestamp} (expected {KnownTimestamp})."); - var tup = (Unknown1, Unknown2, Unknown3, Unknown4, Unknown5); Check.Data(!strict || tup == (0, 0, 0, 0, 0), $"Unexpected data center type tree values {tup}."); } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask WriteAsync(StreamBinaryWriter writer, CancellationToken cancellationToken) + public async ValueTask WriteAsync( + StreamBinaryWriter writer, DataCenterFormat format, CancellationToken cancellationToken) { - await writer.WriteInt32Async(KnownVersion, cancellationToken).ConfigureAwait(false); + var is6 = format != DataCenterFormat.V3; + + await writer.WriteInt32Async(is6 ? 6 : 3, cancellationToken).ConfigureAwait(false); await writer.WriteDoubleAsync(KnownTimestamp, cancellationToken).ConfigureAwait(false); - await writer.WriteInt32Async(Revision, cancellationToken).ConfigureAwait(false); + + if (is6) + await writer.WriteInt32Async(Revision, cancellationToken).ConfigureAwait(false); + await writer.WriteInt16Async(Unknown1, cancellationToken).ConfigureAwait(false); await writer.WriteInt16Async(Unknown2, cancellationToken).ConfigureAwait(false); await writer.WriteInt32Async(Unknown3, cancellationToken).ConfigureAwait(false); diff --git a/src/formats/Data/Serialization/DataCenterWriter.cs b/src/formats/Data/Serialization/DataCenterWriter.cs index b9ed26a..61158cb 100644 --- a/src/formats/Data/Serialization/DataCenterWriter.cs +++ b/src/formats/Data/Serialization/DataCenterWriter.cs @@ -378,13 +378,16 @@ public Task WriteAsync(Stream stream, DataCenterNode root, CancellationToken can using var memoryStream = new MemoryStream(); var writer = new StreamBinaryWriter(memoryStream); - - await _header.WriteAsync(writer, cancellationToken).ConfigureAwait(false); - await _keys.WriteAsync(writer, cancellationToken).ConfigureAwait(false); - await _attributes.WriteAsync(writer, cancellationToken).ConfigureAwait(false); - await _nodes.WriteAsync(writer, cancellationToken).ConfigureAwait(false); - await _values.WriteAsync(writer, cancellationToken).ConfigureAwait(false); - await _names.WriteAsync(writer, cancellationToken).ConfigureAwait(false); + var arch = _options.Format == DataCenterFormat.V6X64 + ? DataCenterArchitecture.X64 + : DataCenterArchitecture.X86; + + await _header.WriteAsync(writer, _options.Format, cancellationToken).ConfigureAwait(false); + await _keys.WriteAsync(arch, writer, cancellationToken).ConfigureAwait(false); + await _attributes.WriteAsync(arch, writer, cancellationToken).ConfigureAwait(false); + await _nodes.WriteAsync(arch, writer, cancellationToken).ConfigureAwait(false); + await _values.WriteAsync(arch, writer, cancellationToken).ConfigureAwait(false); + await _names.WriteAsync(arch, writer, cancellationToken).ConfigureAwait(false); await _footer.WriteAsync(writer, cancellationToken).ConfigureAwait(false); await memoryStream.FlushAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/formats/Data/Serialization/Items/DataCenterRawAddress.cs b/src/formats/Data/Serialization/Items/DataCenterRawAddress.cs index 2edf7ad..d8766d2 100644 --- a/src/formats/Data/Serialization/Items/DataCenterRawAddress.cs +++ b/src/formats/Data/Serialization/Items/DataCenterRawAddress.cs @@ -7,15 +7,26 @@ internal struct DataCenterRawAddress : IDataCenterItem, IEquatable left.Equals(right); + + public static bool operator !=(DataCenterRawAddress left, DataCenterRawAddress right) => !left.Equals(right); + + public static unsafe int GetSize(DataCenterArchitecture architecture) { - SegmentIndex = BinaryPrimitives.ReverseEndianness(SegmentIndex); - ElementIndex = BinaryPrimitives.ReverseEndianness(ElementIndex); + return sizeof(DataCenterRawAddress); } - public static bool operator ==(DataCenterRawAddress left, DataCenterRawAddress right) => left.Equals(right); + public void Read(DataCenterArchitecture architecture, ref SpanReader reader) + { + SegmentIndex = reader.ReadUInt16(); + ElementIndex = reader.ReadUInt16(); + } - public static bool operator !=(DataCenterRawAddress left, DataCenterRawAddress right) => !left.Equals(right); + public readonly void Write(DataCenterArchitecture architecture, ref SpanWriter writer) + { + writer.WriteUInt16(SegmentIndex); + writer.WriteUInt16(ElementIndex); + } public readonly bool Equals(DataCenterRawAddress other) { diff --git a/src/formats/Data/Serialization/Items/DataCenterRawAttribute.cs b/src/formats/Data/Serialization/Items/DataCenterRawAttribute.cs index 37fc580..c91e834 100644 --- a/src/formats/Data/Serialization/Items/DataCenterRawAttribute.cs +++ b/src/formats/Data/Serialization/Items/DataCenterRawAttribute.cs @@ -9,20 +9,38 @@ internal struct DataCenterRawAttribute : IDataCenterItem, IEquatable left.Equals(right); + + public static bool operator !=(DataCenterRawAttribute left, DataCenterRawAttribute right) => !left.Equals(right); + + public static unsafe int GetSize(DataCenterArchitecture architecture) { - NameIndex = BinaryPrimitives.ReverseEndianness(NameIndex); - TypeInfo = BinaryPrimitives.ReverseEndianness(TypeInfo); - Value = BinaryPrimitives.ReverseEndianness(Value); + return architecture == DataCenterArchitecture.X64 + ? sizeof(DataCenterRawAttribute) + : sizeof(DataCenterRawAttribute) - sizeof(uint); + } - // Note: Padding1 can be safely ignored. + public void Read(DataCenterArchitecture architecture, ref SpanReader reader) + { + NameIndex = reader.ReadUInt16(); + TypeInfo = reader.ReadUInt16(); + Value = reader.ReadInt32(); + + if (architecture == DataCenterArchitecture.X64) + reader.Advance(sizeof(uint)); } - public static bool operator ==(DataCenterRawAttribute left, DataCenterRawAttribute right) => left.Equals(right); + public readonly void Write(DataCenterArchitecture architecture, ref SpanWriter writer) + { + writer.WriteUInt16(NameIndex); + writer.WriteUInt16(TypeInfo); + writer.WriteInt32(Value); - public static bool operator !=(DataCenterRawAttribute left, DataCenterRawAttribute right) => !left.Equals(right); + if (architecture == DataCenterArchitecture.X64) + writer.Advance(sizeof(uint)); + } public readonly bool Equals(DataCenterRawAttribute other) { diff --git a/src/formats/Data/Serialization/Items/DataCenterRawChar.cs b/src/formats/Data/Serialization/Items/DataCenterRawChar.cs index 63d720a..489ac85 100644 --- a/src/formats/Data/Serialization/Items/DataCenterRawChar.cs +++ b/src/formats/Data/Serialization/Items/DataCenterRawChar.cs @@ -5,8 +5,18 @@ internal struct DataCenterRawChar : IDataCenterItem { public char Value; - public void ReverseEndianness() + public static unsafe int GetSize(DataCenterArchitecture architecture) { - Value = (char)BinaryPrimitives.ReverseEndianness(Value); + return sizeof(DataCenterRawChar); + } + + public void Read(DataCenterArchitecture architecture, ref SpanReader reader) + { + Value = reader.ReadChar(); + } + + public readonly void Write(DataCenterArchitecture architecture, ref SpanWriter writer) + { + writer.WriteChar(Value); } } diff --git a/src/formats/Data/Serialization/Items/DataCenterRawKeys.cs b/src/formats/Data/Serialization/Items/DataCenterRawKeys.cs index abdcebd..1c8e98a 100644 --- a/src/formats/Data/Serialization/Items/DataCenterRawKeys.cs +++ b/src/formats/Data/Serialization/Items/DataCenterRawKeys.cs @@ -11,11 +11,24 @@ internal struct DataCenterRawKeys : IDataCenterItem public ushort NameIndex4; - public void ReverseEndianness() + public static unsafe int GetSize(DataCenterArchitecture architecture) { - NameIndex1 = BinaryPrimitives.ReverseEndianness(NameIndex1); - NameIndex2 = BinaryPrimitives.ReverseEndianness(NameIndex2); - NameIndex3 = BinaryPrimitives.ReverseEndianness(NameIndex3); - NameIndex4 = BinaryPrimitives.ReverseEndianness(NameIndex4); + return sizeof(DataCenterRawKeys); + } + + public void Read(DataCenterArchitecture architecture, ref SpanReader reader) + { + NameIndex1 = reader.ReadUInt16(); + NameIndex2 = reader.ReadUInt16(); + NameIndex3 = reader.ReadUInt16(); + NameIndex4 = reader.ReadUInt16(); + } + + public readonly void Write(DataCenterArchitecture architecture, ref SpanWriter writer) + { + writer.WriteUInt16(NameIndex1); + writer.WriteUInt16(NameIndex2); + writer.WriteUInt16(NameIndex3); + writer.WriteUInt16(NameIndex4); } } diff --git a/src/formats/Data/Serialization/Items/DataCenterRawNode.cs b/src/formats/Data/Serialization/Items/DataCenterRawNode.cs index be3f9b2..5b2b5fc 100644 --- a/src/formats/Data/Serialization/Items/DataCenterRawNode.cs +++ b/src/formats/Data/Serialization/Items/DataCenterRawNode.cs @@ -13,27 +13,56 @@ internal struct DataCenterRawNode : IDataCenterItem, IEquatable left.Equals(right); + + public static bool operator !=(DataCenterRawNode left, DataCenterRawNode right) => !left.Equals(right); + + public static unsafe int GetSize(DataCenterArchitecture architecture) { - NameIndex = BinaryPrimitives.ReverseEndianness(NameIndex); - KeysInfo = BinaryPrimitives.ReverseEndianness(KeysInfo); - AttributeCount = BinaryPrimitives.ReverseEndianness(AttributeCount); - ChildCount = BinaryPrimitives.ReverseEndianness(ChildCount); - AttributeAddress.ReverseEndianness(); - ChildAddress.ReverseEndianness(); - - // Note: Padding1 and Padding2 can be safely ignored. + return architecture == DataCenterArchitecture.X64 + ? sizeof(DataCenterRawNode) + : sizeof(DataCenterRawNode) - sizeof(uint) * 2; } - public static bool operator ==(DataCenterRawNode left, DataCenterRawNode right) => left.Equals(right); + public void Read(DataCenterArchitecture architecture, ref SpanReader reader) + { + NameIndex = reader.ReadUInt16(); + KeysInfo = reader.ReadUInt16(); + AttributeCount = reader.ReadUInt16(); + ChildCount = reader.ReadUInt16(); + AttributeAddress.Read(architecture, ref reader); - public static bool operator !=(DataCenterRawNode left, DataCenterRawNode right) => !left.Equals(right); + if (architecture == DataCenterArchitecture.X64) + reader.Advance(sizeof(uint)); + + ChildAddress.Read(architecture, ref reader); + + if (architecture == DataCenterArchitecture.X64) + reader.Advance(sizeof(uint)); + } + + public readonly void Write(DataCenterArchitecture architecture, ref SpanWriter writer) + { + writer.WriteUInt16(NameIndex); + writer.WriteUInt16(KeysInfo); + writer.WriteUInt16(AttributeCount); + writer.WriteUInt16(ChildCount); + AttributeAddress.Write(architecture, ref writer); + + if (architecture == DataCenterArchitecture.X64) + writer.Advance(sizeof(uint)); + + ChildAddress.Write(architecture, ref writer); + + if (architecture == DataCenterArchitecture.X64) + writer.Advance(sizeof(uint)); + } public readonly bool Equals(DataCenterRawNode other) { diff --git a/src/formats/Data/Serialization/Items/DataCenterRawString.cs b/src/formats/Data/Serialization/Items/DataCenterRawString.cs index b36ed9b..d00c62e 100644 --- a/src/formats/Data/Serialization/Items/DataCenterRawString.cs +++ b/src/formats/Data/Serialization/Items/DataCenterRawString.cs @@ -11,11 +11,24 @@ internal struct DataCenterRawString : IDataCenterItem public DataCenterRawAddress Address; - public void ReverseEndianness() + public static unsafe int GetSize(DataCenterArchitecture architecture) { - Hash = BinaryPrimitives.ReverseEndianness(Hash); - Length = BinaryPrimitives.ReverseEndianness(Length); - Index = BinaryPrimitives.ReverseEndianness(Index); - Address.ReverseEndianness(); + return sizeof(DataCenterRawString); + } + + public void Read(DataCenterArchitecture architecture, ref SpanReader reader) + { + Hash = reader.ReadUInt32(); + Length = reader.ReadInt32(); + Index = reader.ReadInt32(); + Address.Read(architecture, ref reader); + } + + public readonly void Write(DataCenterArchitecture architecture, ref SpanWriter writer) + { + writer.WriteUInt32(Hash); + writer.WriteInt32(Length); + writer.WriteInt32(Index); + Address.Write(architecture, ref writer); } } diff --git a/src/formats/Data/Serialization/Items/IDataCenterItem.cs b/src/formats/Data/Serialization/Items/IDataCenterItem.cs index 0f437f0..afa3b3d 100644 --- a/src/formats/Data/Serialization/Items/IDataCenterItem.cs +++ b/src/formats/Data/Serialization/Items/IDataCenterItem.cs @@ -2,5 +2,9 @@ namespace Vezel.Novadrop.Data.Serialization.Items; internal interface IDataCenterItem { - public abstract void ReverseEndianness(); + public static abstract int GetSize(DataCenterArchitecture architecture); + + public abstract void Read(DataCenterArchitecture architecture, ref SpanReader reader); + + public abstract void Write(DataCenterArchitecture architecture, ref SpanWriter writer); } diff --git a/src/formats/Data/Serialization/Readers/DataCenterReader.cs b/src/formats/Data/Serialization/Readers/DataCenterReader.cs index 147bdab..ab27c59 100644 --- a/src/formats/Data/Serialization/Readers/DataCenterReader.cs +++ b/src/formats/Data/Serialization/Readers/DataCenterReader.cs @@ -215,14 +215,15 @@ public Task ReadAsync(Stream stream, CancellationToken cancellat await using (zlibStream.ConfigureAwait(false)) { var reader = new StreamBinaryReader(zlibStream); + var arch = _options.Architecture; var strict = _options.Strict; await _header.ReadAsync(strict, reader, cancellationToken).ConfigureAwait(false); - await _keys.ReadAsync(reader, cancellationToken).ConfigureAwait(false); - await _attributes.ReadAsync(strict, reader, cancellationToken).ConfigureAwait(false); - await _nodes.ReadAsync(strict, reader, cancellationToken).ConfigureAwait(false); - await _values.ReadAsync(strict, reader, cancellationToken).ConfigureAwait(false); - await _names.ReadAsync(strict, reader, cancellationToken).ConfigureAwait(false); + await _keys.ReadAsync(arch, reader, cancellationToken).ConfigureAwait(false); + await _attributes.ReadAsync(arch, reader, cancellationToken).ConfigureAwait(false); + await _nodes.ReadAsync(arch, reader, cancellationToken).ConfigureAwait(false); + await _values.ReadAsync(arch, strict, reader, cancellationToken).ConfigureAwait(false); + await _names.ReadAsync(arch, strict, reader, cancellationToken).ConfigureAwait(false); _keys.Populate(); diff --git a/src/formats/Data/Serialization/Regions/DataCenterRegion`1.cs b/src/formats/Data/Serialization/Regions/DataCenterRegion`1.cs index 4621b2f..4e68871 100644 --- a/src/formats/Data/Serialization/Regions/DataCenterRegion`1.cs +++ b/src/formats/Data/Serialization/Regions/DataCenterRegion`1.cs @@ -8,39 +8,39 @@ internal sealed class DataCenterRegion public List Elements { get; } = new(ushort.MaxValue); [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask ReadAsync(bool strict, StreamBinaryReader reader, CancellationToken cancellationToken) + public async ValueTask ReadAsync( + DataCenterArchitecture architecture, StreamBinaryReader reader, CancellationToken cancellationToken) { var capacity = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false); var count = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false); Check.Data(count >= 0, $"Region length {count} is negative."); + Check.Data(capacity >= 0, $"Region capacity {capacity} is negative."); + Check.Data(count <= capacity, $"Region length {count} is greater than region capacity {capacity}."); - if (strict) - { - Check.Data(capacity >= 0, $"Region capacity {capacity} is negative."); - Check.Data(count <= capacity, $"Region length {count} is greater than region capacity {capacity}."); - } - - var length = Unsafe.SizeOf() * capacity; + var length = T.GetSize(architecture) * capacity; var bytes = ArrayPool.Shared.Rent(length); try { await reader.ReadAsync(bytes.AsMemory(0, length), cancellationToken).ConfigureAwait(false); - void ProcessElements() + void ReadElements() { - foreach (ref var elem in MemoryMarshal.Cast(bytes)[..count]) + var reader = new SpanReader(bytes); + + for (var i = 0; i < count; i++) { - if (!BitConverter.IsLittleEndian) - elem.ReverseEndianness(); + var elem = default(T); + + elem.Read(architecture, ref reader); Elements.Add(elem); } } // Cannot use refs in async methods... - ProcessElements(); + ReadElements(); } finally { @@ -49,33 +49,29 @@ void ProcessElements() } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask WriteAsync(StreamBinaryWriter writer, CancellationToken cancellationToken) + public async ValueTask WriteAsync( + DataCenterArchitecture architecture, StreamBinaryWriter writer, CancellationToken cancellationToken) { var count = Elements.Count; for (var i = 0; i < 2; i++) await writer.WriteInt32Async(count, cancellationToken).ConfigureAwait(false); - var length = Unsafe.SizeOf() * count; + var length = T.GetSize(architecture) * count; var bytes = ArrayPool.Shared.Rent(length); try { - void ProcessElements() + void WriteElements() { - var i = 0; - - foreach (ref var elem in MemoryMarshal.Cast(bytes)[..count]) - { - elem = Elements[i++]; + var writer = new SpanWriter(bytes); - if (!BitConverter.IsLittleEndian) - elem.ReverseEndianness(); - } + foreach (var elem in Elements) + elem.Write(architecture, ref writer); } // Cannot use refs in async methods... - ProcessElements(); + WriteElements(); await writer.WriteAsync(bytes.AsMemory(0, length), cancellationToken).ConfigureAwait(false); } diff --git a/src/formats/Data/Serialization/Regions/DataCenterSegmentedRegion`1.cs b/src/formats/Data/Serialization/Regions/DataCenterSegmentedRegion`1.cs index d0830ad..e65124a 100644 --- a/src/formats/Data/Serialization/Regions/DataCenterSegmentedRegion`1.cs +++ b/src/formats/Data/Serialization/Regions/DataCenterSegmentedRegion`1.cs @@ -8,7 +8,8 @@ internal sealed class DataCenterSegmentedRegion public List> Segments { get; } = new(ushort.MaxValue); [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask ReadAsync(bool strict, StreamBinaryReader reader, CancellationToken cancellationToken) + public async ValueTask ReadAsync( + DataCenterArchitecture architecture, StreamBinaryReader reader, CancellationToken cancellationToken) { var count = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false); @@ -18,19 +19,20 @@ public async ValueTask ReadAsync(bool strict, StreamBinaryReader reader, Cancell { var region = new DataCenterRegion(); - await region.ReadAsync(strict, reader, cancellationToken).ConfigureAwait(false); + await region.ReadAsync(architecture, reader, cancellationToken).ConfigureAwait(false); Segments.Add(region); } } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask WriteAsync(StreamBinaryWriter writer, CancellationToken cancellationToken) + public async ValueTask WriteAsync( + DataCenterArchitecture architecture, StreamBinaryWriter writer, CancellationToken cancellationToken) { await writer.WriteInt32Async(Segments.Count, cancellationToken).ConfigureAwait(false); foreach (var region in Segments) - await region.WriteAsync(writer, cancellationToken).ConfigureAwait(false); + await region.WriteAsync(architecture, writer, cancellationToken).ConfigureAwait(false); } public T GetElement(DataCenterAddress address) diff --git a/src/formats/Data/Serialization/Regions/DataCenterSegmentedSimpleRegion`1.cs b/src/formats/Data/Serialization/Regions/DataCenterSegmentedSimpleRegion`1.cs index 7148827..29b0c14 100644 --- a/src/formats/Data/Serialization/Regions/DataCenterSegmentedSimpleRegion`1.cs +++ b/src/formats/Data/Serialization/Regions/DataCenterSegmentedSimpleRegion`1.cs @@ -18,17 +18,19 @@ public DataCenterSegmentedSimpleRegion(int count) } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask ReadAsync(StreamBinaryReader reader, CancellationToken cancellationToken) + public async ValueTask ReadAsync( + DataCenterArchitecture architecture, StreamBinaryReader reader, CancellationToken cancellationToken) { foreach (var region in Segments) - await region.ReadAsync(reader, cancellationToken).ConfigureAwait(false); + await region.ReadAsync(architecture, reader, cancellationToken).ConfigureAwait(false); } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask WriteAsync(StreamBinaryWriter writer, CancellationToken cancellationToken) + public async ValueTask WriteAsync( + DataCenterArchitecture architecture, StreamBinaryWriter writer, CancellationToken cancellationToken) { foreach (var region in Segments) - await region.WriteAsync(writer, cancellationToken).ConfigureAwait(false); + await region.WriteAsync(architecture, writer, cancellationToken).ConfigureAwait(false); } public T GetElement(DataCenterAddress address) diff --git a/src/formats/Data/Serialization/Regions/DataCenterSimpleRegion`1.cs b/src/formats/Data/Serialization/Regions/DataCenterSimpleRegion`1.cs index ae40ebf..d728d8f 100644 --- a/src/formats/Data/Serialization/Regions/DataCenterSimpleRegion`1.cs +++ b/src/formats/Data/Serialization/Regions/DataCenterSimpleRegion`1.cs @@ -15,7 +15,8 @@ public DataCenterSimpleRegion(bool offByOne) } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask ReadAsync(StreamBinaryReader reader, CancellationToken cancellationToken) + public async ValueTask ReadAsync( + DataCenterArchitecture architecture, StreamBinaryReader reader, CancellationToken cancellationToken) { var count = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false); @@ -24,26 +25,29 @@ public async ValueTask ReadAsync(StreamBinaryReader reader, CancellationToken ca Check.Data(count >= 0, $"Region length {count} is negative."); - var length = Unsafe.SizeOf() * count; + var length = T.GetSize(architecture) * count; var bytes = ArrayPool.Shared.Rent(length); try { await reader.ReadAsync(bytes.AsMemory(0, length), cancellationToken).ConfigureAwait(false); - void ProcessElements() + void ReadElements() { - foreach (ref var elem in MemoryMarshal.Cast(bytes)[..count]) + var reader = new SpanReader(bytes); + + for (var i = 0; i < count; i++) { - if (!BitConverter.IsLittleEndian) - elem.ReverseEndianness(); + var elem = default(T); + + elem.Read(architecture, ref reader); Elements.Add(elem); } } // Cannot use refs in async methods... - ProcessElements(); + ReadElements(); } finally { @@ -52,32 +56,28 @@ void ProcessElements() } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask WriteAsync(StreamBinaryWriter writer, CancellationToken cancellationToken) + public async ValueTask WriteAsync( + DataCenterArchitecture architecture, StreamBinaryWriter writer, CancellationToken cancellationToken) { var count = Elements.Count; await writer.WriteInt32Async(count + (_offByOne ? 1 : 0), cancellationToken).ConfigureAwait(false); - var length = Unsafe.SizeOf() * count; + var length = T.GetSize(architecture) * count; var bytes = ArrayPool.Shared.Rent(length); try { - void ProcessElements() + void WriteElements() { - var i = 0; + var writer = new SpanWriter(bytes); - foreach (ref var elem in MemoryMarshal.Cast(bytes)[..count]) - { - elem = Elements[i++]; - - if (!BitConverter.IsLittleEndian) - elem.ReverseEndianness(); - } + foreach (var elem in Elements) + elem.Write(architecture, ref writer); } // Cannot use refs in async methods... - ProcessElements(); + WriteElements(); await writer.WriteAsync(bytes.AsMemory(0, length), cancellationToken).ConfigureAwait(false); } diff --git a/src/formats/Data/Serialization/Tables/DataCenterKeysTableReader.cs b/src/formats/Data/Serialization/Tables/DataCenterKeysTableReader.cs index d62112b..db7da63 100644 --- a/src/formats/Data/Serialization/Tables/DataCenterKeysTableReader.cs +++ b/src/formats/Data/Serialization/Tables/DataCenterKeysTableReader.cs @@ -17,9 +17,10 @@ public DataCenterKeysTableReader(DataCenterStringTableReader names) } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask ReadAsync(StreamBinaryReader reader, CancellationToken cancellationToken) + public async ValueTask ReadAsync( + DataCenterArchitecture architecture, StreamBinaryReader reader, CancellationToken cancellationToken) { - await _keys.ReadAsync(reader, cancellationToken).ConfigureAwait(false); + await _keys.ReadAsync(architecture, reader, cancellationToken).ConfigureAwait(false); } public void Populate() diff --git a/src/formats/Data/Serialization/Tables/DataCenterKeysTableWriter.cs b/src/formats/Data/Serialization/Tables/DataCenterKeysTableWriter.cs index b856e90..e42172d 100644 --- a/src/formats/Data/Serialization/Tables/DataCenterKeysTableWriter.cs +++ b/src/formats/Data/Serialization/Tables/DataCenterKeysTableWriter.cs @@ -17,9 +17,10 @@ public DataCenterKeysTableWriter(DataCenterStringTableWriter names) } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask WriteAsync(StreamBinaryWriter writer, CancellationToken cancellationToken) + public async ValueTask WriteAsync( + DataCenterArchitecture architecture, StreamBinaryWriter writer, CancellationToken cancellationToken) { - await _keys.WriteAsync(writer, cancellationToken).ConfigureAwait(false); + await _keys.WriteAsync(architecture, writer, cancellationToken).ConfigureAwait(false); } public int AddKeys(string? attributeName1, string? attributeName2, string? attributeName3, string? attributeName4) diff --git a/src/formats/Data/Serialization/Tables/DataCenterStringTableReader.cs b/src/formats/Data/Serialization/Tables/DataCenterStringTableReader.cs index ae80b98..8c18708 100644 --- a/src/formats/Data/Serialization/Tables/DataCenterStringTableReader.cs +++ b/src/formats/Data/Serialization/Tables/DataCenterStringTableReader.cs @@ -21,11 +21,15 @@ public DataCenterStringTableReader(int count) } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask ReadAsync(bool strict, StreamBinaryReader reader, CancellationToken cancellationToken) + public async ValueTask ReadAsync( + DataCenterArchitecture architecture, + bool strict, + StreamBinaryReader reader, + CancellationToken cancellationToken) { - await _data.ReadAsync(strict, reader, cancellationToken).ConfigureAwait(false); - await _strings.ReadAsync(reader, cancellationToken).ConfigureAwait(false); - await _addresses.ReadAsync(reader, cancellationToken).ConfigureAwait(false); + await _data.ReadAsync(architecture, reader, cancellationToken).ConfigureAwait(false); + await _strings.ReadAsync(architecture, reader, cancellationToken).ConfigureAwait(false); + await _addresses.ReadAsync(architecture, reader, cancellationToken).ConfigureAwait(false); Check.Data( !strict || _data.Segments.Count <= DataCenterAddress.MaxValue.SegmentIndex, diff --git a/src/formats/Data/Serialization/Tables/DataCenterStringTableWriter.cs b/src/formats/Data/Serialization/Tables/DataCenterStringTableWriter.cs index 3297197..a2ff038 100644 --- a/src/formats/Data/Serialization/Tables/DataCenterStringTableWriter.cs +++ b/src/formats/Data/Serialization/Tables/DataCenterStringTableWriter.cs @@ -22,15 +22,16 @@ public DataCenterStringTableWriter(int count, bool limit) } [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public async ValueTask WriteAsync(StreamBinaryWriter writer, CancellationToken cancellationToken) + public async ValueTask WriteAsync( + DataCenterArchitecture architecture, StreamBinaryWriter writer, CancellationToken cancellationToken) { - await _data.WriteAsync(writer, cancellationToken).ConfigureAwait(false); + await _data.WriteAsync(architecture, writer, cancellationToken).ConfigureAwait(false); foreach (var seg in _strings.Segments) seg.Elements.Sort(static (a, b) => a.Hash.CompareTo(b.Hash)); - await _strings.WriteAsync(writer, cancellationToken).ConfigureAwait(false); - await _addresses.WriteAsync(writer, cancellationToken).ConfigureAwait(false); + await _strings.WriteAsync(architecture, writer, cancellationToken).ConfigureAwait(false); + await _addresses.WriteAsync(architecture, writer, cancellationToken).ConfigureAwait(false); } public DataCenterRawString AddString(string value) diff --git a/src/formats/formats.csproj b/src/formats/formats.csproj index fb31cc0..62a22e2 100644 --- a/src/formats/formats.csproj +++ b/src/formats/formats.csproj @@ -16,6 +16,7 @@ This package provides support for TERA's various file formats. + diff --git a/src/tools/dc/Commands/PackCommand.cs b/src/tools/dc/Commands/PackCommand.cs index ce4b7c3..2203dec 100644 --- a/src/tools/dc/Commands/PackCommand.cs +++ b/src/tools/dc/Commands/PackCommand.cs @@ -13,6 +13,10 @@ public sealed class PackCommandSettings : CommandSettings [Description("Output file")] public string Output { get; } + [CommandOption("--format ")] + [Description("Set format variant")] + public DataCenterFormat Format { get; init; } = DataCenterFormat.V6X64; + [CommandOption("--revision ")] [Description("Set data revision")] public int Revision { get; init; } = DataCenter.LatestRevision; @@ -252,6 +256,7 @@ await DataCenter.SaveAsync( root, stream, new DataCenterSaveOptions() + .WithFormat(settings.Format) .WithRevision(settings.Revision) .WithCompressionLevel(settings.Compression) .WithKey(settings.EncryptionKey.Span) diff --git a/src/tools/dc/Commands/RepackCommand.cs b/src/tools/dc/Commands/RepackCommand.cs index faf1751..05f79b4 100644 --- a/src/tools/dc/Commands/RepackCommand.cs +++ b/src/tools/dc/Commands/RepackCommand.cs @@ -23,10 +23,18 @@ public sealed class RepackCommandSettings : CommandSettings [TypeConverter(typeof(HexStringConverter))] public ReadOnlyMemory DecryptionIV { get; init; } = DataCenter.LatestIV; + [CommandOption("--architecture ")] + [Description("Set format architecture")] + public DataCenterArchitecture Architecture { get; init; } = DataCenterArchitecture.X64; + [CommandOption("--strict")] [Description("Enable strict verification")] public bool Strict { get; init; } + [CommandOption("--format ")] + [Description("Set format variant")] + public DataCenterFormat Format { get; init; } = DataCenterFormat.V6X64; + [CommandOption("--revision ")] [Description("Set data revision")] public int Revision { get; init; } = DataCenter.LatestRevision; @@ -68,6 +76,7 @@ protected override async Task ExecuteAsync( new DataCenterLoadOptions() .WithKey(settings.DecryptionKey.Span) .WithIV(settings.DecryptionIV.Span) + .WithArchitecture(settings.Architecture) .WithStrict(settings.Strict) .WithLoaderMode(DataCenterLoaderMode.Eager) .WithMutability(DataCenterMutability.Immutable), @@ -84,6 +93,7 @@ await DataCenter.SaveAsync( root, stream, new DataCenterSaveOptions() + .WithFormat(settings.Format) .WithRevision(settings.Revision) .WithCompressionLevel(settings.Compression) .WithKey(settings.EncryptionKey.Span) diff --git a/src/tools/dc/Commands/UnpackCommand.cs b/src/tools/dc/Commands/UnpackCommand.cs index 1649993..735f850 100644 --- a/src/tools/dc/Commands/UnpackCommand.cs +++ b/src/tools/dc/Commands/UnpackCommand.cs @@ -23,6 +23,10 @@ public sealed class UnpackCommandSettings : CommandSettings [TypeConverter(typeof(HexStringConverter))] public ReadOnlyMemory DecryptionIV { get; init; } = DataCenter.LatestIV; + [CommandOption("--architecture ")] + [Description("Set format architecture")] + public DataCenterArchitecture Architecture { get; init; } = DataCenterArchitecture.X64; + [CommandOption("--strict")] [Description("Enable strict verification")] public bool Strict { get; init; } @@ -59,6 +63,7 @@ protected override async Task ExecuteAsync( new DataCenterLoadOptions() .WithKey(settings.DecryptionKey.Span) .WithIV(settings.DecryptionIV.Span) + .WithArchitecture(settings.Architecture) .WithStrict(settings.Strict), cancellationToken); }); diff --git a/src/tools/dc/Commands/VerifyCommand.cs b/src/tools/dc/Commands/VerifyCommand.cs index 5d77806..933b6bd 100644 --- a/src/tools/dc/Commands/VerifyCommand.cs +++ b/src/tools/dc/Commands/VerifyCommand.cs @@ -19,6 +19,10 @@ public sealed class VerifyCommandSettings : CommandSettings [TypeConverter(typeof(HexStringConverter))] public ReadOnlyMemory DecryptionIV { get; init; } = DataCenter.LatestIV; + [CommandOption("--architecture ")] + [Description("Set format architecture")] + public DataCenterArchitecture Architecture { get; init; } = DataCenterArchitecture.X64; + [CommandOption("--strict")] [Description("Enable strict verification")] public bool Strict { get; init; } @@ -69,6 +73,7 @@ string ComputeHash(HashAlgorithm algorithm) new DataCenterLoadOptions() .WithKey(settings.DecryptionKey.Span) .WithIV(settings.DecryptionIV.Span) + .WithArchitecture(settings.Architecture) .WithStrict(settings.Strict), cancellationToken); });