Skip to content

Commit b3f8052

Browse files
Rebuilt CPK tools (#20)
- Rebuilt how CPKs are read and written to, speeding up CPK repacking and massively reducing allocations
1 parent 9ca7fc3 commit b3f8052

File tree

21 files changed

+1426
-1391
lines changed

21 files changed

+1426
-1391
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,3 +495,4 @@ fabric.properties
495495
### Custom ###
496496
launchSettings.json
497497
.idea/.idea.ShinRyuModManager-CE/.idea/sonarlint.xml
498+
dist/

CpkTools/ColumnFlags.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace CpkTools;
2+
3+
public enum StorageFlags {
4+
StorageMask = 0xF0,
5+
StorageNone = 0x00,
6+
StorageZero = 0x10,
7+
StorageConstant = 0x30,
8+
StoragePerrow = 0x50,
9+
}
10+
11+
public enum TypeFlags {
12+
TypeMask = 0x0F,
13+
TypeData = 0x0B,
14+
TypeString = 0x0A,
15+
TypeFloat = 0x08,
16+
Type8Byte2 = 0x07,
17+
Type8Byte = 0x06,
18+
Type4Byte2 = 0x05,
19+
Type4Byte = 0x04,
20+
Type2Byte2 = 0x03,
21+
Type2Byte = 0x02,
22+
Type1Byte2 = 0x01,
23+
Type1Byte = 0x00,
24+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using CpkTools.Model;
2+
3+
namespace CpkTools.Comparers;
4+
5+
public sealed class FileEntryIdComparer : IComparer<FileEntry> {
6+
public static readonly FileEntryIdComparer Instance = new();
7+
8+
public int Compare(FileEntry? x, FileEntry? y) {
9+
if (ReferenceEquals(x, y)) return 0;
10+
if (x is null) return -1;
11+
if (y is null) return 1;
12+
13+
return x.Id.CompareTo(y.Id);
14+
}
15+
}

CpkTools/CpkTools.csproj

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.4.0" />
11+
</ItemGroup>
12+
13+
</Project>

CpkTools/Endian/EndianReader.cs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
using System.Buffers.Binary;
2+
using CommunityToolkit.HighPerformance;
3+
4+
namespace CpkTools.Endian;
5+
6+
public sealed class EndianReader : IDisposable {
7+
public bool IsLittleEndian { get; set; }
8+
public long Position { get => BaseStream.Position; }
9+
public Stream BaseStream { get; }
10+
11+
public EndianReader(Memory<byte> data, bool isLittleEndian = false) {
12+
BaseStream = data.AsStream();
13+
IsLittleEndian = isLittleEndian;
14+
}
15+
16+
public EndianReader(byte[] data, bool isLittleEndian = false) {
17+
BaseStream = new MemoryStream(data);
18+
IsLittleEndian = isLittleEndian;
19+
}
20+
21+
public EndianReader(Stream stream, bool isLittleEndian = false) {
22+
BaseStream = stream;
23+
IsLittleEndian = isLittleEndian;
24+
}
25+
26+
public Half ReadHalf() {
27+
Span<byte> buffer = stackalloc byte[2];
28+
29+
BaseStream.ReadExactly(buffer);
30+
31+
return IsLittleEndian
32+
? BinaryPrimitives.ReadHalfLittleEndian(buffer)
33+
: BinaryPrimitives.ReadHalfBigEndian(buffer);
34+
}
35+
36+
public float ReadSingle() {
37+
Span<byte> buffer = stackalloc byte[4];
38+
39+
BaseStream.ReadExactly(buffer);
40+
41+
return IsLittleEndian
42+
? BinaryPrimitives.ReadSingleLittleEndian(buffer)
43+
: BinaryPrimitives.ReadSingleBigEndian(buffer);
44+
}
45+
46+
public double ReadDouble() {
47+
Span<byte> buffer = stackalloc byte[8];
48+
49+
BaseStream.ReadExactly(buffer);
50+
51+
return IsLittleEndian
52+
? BinaryPrimitives.ReadDoubleLittleEndian(buffer)
53+
: BinaryPrimitives.ReadDoubleBigEndian(buffer);
54+
}
55+
56+
public short ReadInt16() {
57+
Span<byte> buffer = stackalloc byte[2];
58+
59+
BaseStream.ReadExactly(buffer);
60+
61+
return IsLittleEndian
62+
? BinaryPrimitives.ReadInt16LittleEndian(buffer)
63+
: BinaryPrimitives.ReadInt16BigEndian(buffer);
64+
}
65+
66+
public int ReadInt32() {
67+
Span<byte> buffer = stackalloc byte[4];
68+
69+
BaseStream.ReadExactly(buffer);
70+
71+
return IsLittleEndian
72+
? BinaryPrimitives.ReadInt32LittleEndian(buffer)
73+
: BinaryPrimitives.ReadInt32BigEndian(buffer);
74+
}
75+
76+
public long ReadInt64() {
77+
Span<byte> buffer = stackalloc byte[8];
78+
79+
BaseStream.ReadExactly(buffer);
80+
81+
return IsLittleEndian
82+
? BinaryPrimitives.ReadInt64LittleEndian(buffer)
83+
: BinaryPrimitives.ReadInt64BigEndian(buffer);
84+
}
85+
86+
public ushort ReadUInt16() {
87+
Span<byte> buffer = stackalloc byte[2];
88+
89+
BaseStream.ReadExactly(buffer);
90+
91+
return IsLittleEndian
92+
? BinaryPrimitives.ReadUInt16LittleEndian(buffer)
93+
: BinaryPrimitives.ReadUInt16BigEndian(buffer);
94+
}
95+
96+
public uint ReadUInt32() {
97+
Span<byte> buffer = stackalloc byte[4];
98+
99+
BaseStream.ReadExactly(buffer);
100+
101+
return IsLittleEndian
102+
? BinaryPrimitives.ReadUInt32LittleEndian(buffer)
103+
: BinaryPrimitives.ReadUInt32BigEndian(buffer);
104+
}
105+
106+
public ulong ReadUInt64() {
107+
Span<byte> buffer = stackalloc byte[8];
108+
109+
BaseStream.ReadExactly(buffer);
110+
111+
return IsLittleEndian
112+
? BinaryPrimitives.ReadUInt64LittleEndian(buffer)
113+
: BinaryPrimitives.ReadUInt64BigEndian(buffer);
114+
}
115+
116+
public byte ReadByte() {
117+
var value = BaseStream.ReadByte();
118+
119+
if (value == -1)
120+
throw new EndOfStreamException();
121+
122+
return (byte)value;
123+
}
124+
125+
public byte[] ReadBytes(int count) {
126+
ArgumentOutOfRangeException.ThrowIfNegative(count);
127+
128+
if (count == 0) {
129+
return [];
130+
}
131+
132+
var result = new byte[count];
133+
var numRead = BaseStream.ReadAtLeast(result, result.Length, throwOnEndOfStream: false);
134+
135+
if (numRead != result.Length) {
136+
// Trim array. This should happen on EOF & possibly net streams.
137+
result = result[..numRead];
138+
}
139+
140+
return result;
141+
}
142+
143+
public int ReadStreamInto(Stream dest, int length) {
144+
ArgumentOutOfRangeException.ThrowIfNegative(length);
145+
146+
if (length == 0) {
147+
return 0;
148+
}
149+
150+
var buffer = new byte[80 * 1024];
151+
var remaining = length;
152+
var totalRead = 0;
153+
154+
while (remaining > 0) {
155+
var toRead = Math.Min(buffer.Length, remaining);
156+
var read = BaseStream.Read(buffer, 0, toRead);
157+
158+
if (read == 0) // EOF
159+
break;
160+
161+
dest.Write(buffer, 0, read);
162+
totalRead += read;
163+
remaining -= read;
164+
}
165+
166+
return totalRead;
167+
}
168+
169+
public void Seek(long offset, SeekOrigin origin) {
170+
BaseStream.Seek(offset, origin);
171+
}
172+
173+
public void Dispose() {
174+
BaseStream.Dispose();
175+
}
176+
}

0 commit comments

Comments
 (0)