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);
});