-
Notifications
You must be signed in to change notification settings - Fork 3
/
GtChunk.cs
439 lines (368 loc) · 13.8 KB
/
GtChunk.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Numerics;
using Bitter;
using FauFau.Formats.GtChunk;
using FauFau.Util;
using SharpCompress.Compressors.Deflate;
using SharpCompress.Compressors.LZMA;
using CompressionLevel = SharpCompress.Compressors.Deflate.CompressionLevel;
namespace FauFau.Formats
{
// Terrain Chunks
// Codes not the most optimised atm so TODO: revisit
public class GtChunkV8 : BinaryWrapper
{
public const int VERSION = 8;
public const ulong NODE_MARKER = 0x12ED5A12ED5B12ED;
public RootNode Root;
public DataBlock[] DataBlocks;
public DatBlock[] DatBlocks;
public LodDataMapping[] LodDataMap;
public void Load(string filePath)
{
var fs = File.OpenRead(filePath);
if (fs == null) return;
using var bs = new BinaryStream(fs, BinaryStream.Endianness.LittleEndian);
Read(bs);
}
// Check if the node id and version match
public bool CheckIsValid(BinaryStream bs)
{
var node = new Node();
node.Read(bs);
var version = bs.Read.UInt();
var isValid = node.NodeMarker == NODE_MARKER && version == VERSION;
return isValid;
}
public override void Read(BinaryStream bs)
{
Root = new RootNode();
Root.Read(bs);
LoadCompressedBlocks(bs);
}
public LodSubChunkData GetDecompressedLod(int lodLevel)
{
var lod = Root.LodNodes[lodLevel];
var lodData = LodDataMap[lodLevel];
var lodDecompressed = new LodSubChunkData()
{
LodData = new byte[lod.UncompressedSize],
SubChunkData = new byte[lod.NumSubchunks][]
};
int idx = 0;
foreach (var subChunkId in lodData.DatBlockIds) {
lodDecompressed.SubChunkData[idx] = GetDecompressedSubChunk(subChunkId).ToArray();
}
return lodDecompressed;
}
public Span<byte> GetDecompressedSubChunk(int SubChunkIdx)
{
var blockDat = DatBlocks[SubChunkIdx];
var decompressedData = blockDat.Decompress();
return decompressedData;
}
public List<NodeDataWrapper> GetSubChunkNodes(int subChunkIdx)
{
var nodes = new List<NodeDataWrapper>();
var sc = GetDecompressedSubChunk(subChunkIdx);
using (var bs = new BinaryStream(new MemoryStream(sc.ToArray()))) {
while (bs.ByteOffset < bs.Length) {
var nodeHeader = ReadNodeHeader(bs);
Console.WriteLine($" Node Offset: {bs.ByteOffset}");
switch ((NodeTypes) nodeHeader.NodeId) {
case NodeTypes.GeoData:
case NodeTypes.GeoData2:
case NodeTypes.GeoData3:
{
var geoData = new GtChunk_MeshData(bs, nodeHeader.Length);
var wrapper = new NodeDataWrapper(nodeHeader.NodeId, geoData);
nodes.Add(wrapper);
break;
}
default:
{
Console.WriteLine($"SubChunk node Id: {nodeHeader.NodeId}");
var nodeData = bs.Read.ByteArray(nodeHeader.Length);
var wrapper = new NodeDataWrapper(nodeHeader.NodeId, nodeData);
nodes.Add(wrapper);
break;
}
}
}
}
return nodes;
}
// load the compressed chunks into memory, doesn't decompress them
private void LoadCompressedBlocks(BinaryStream bs)
{
List<DatBlock> datBlocks = new List<DatBlock>(100);
List<short> datBlockLodMappings = new List<short>(100);
DataBlocks = new DataBlock[Root.NumLods];
LodDataMap = new LodDataMapping[Root.NumLods];
for (int i = 0; i < DataBlocks.Length; i++) {
var lod = Root.LodNodes[i];
LodDataMap[i].DataBlockIdx = i;
// Data blocks
DataBlocks[i] = new DataBlock()
{
CompressedSize = lod.CompressedSize - 4, // id and unk ints
UncompressedSize = lod.UncompressedSize,
LodIdx = i
};
DataBlocks[i].Read(bs);
// Dat blocks
datBlockLodMappings.Clear();
for (int j = 0; j < lod.NumSubchunks; j++) {
var subChunk = lod.SubChunkNodes[j];
datBlockLodMappings.Add((short) datBlocks.Count);
var datBlock = new DatBlock()
{
CompressedSize = subChunk.CompressedSize - (4 + 5), // id and unk ints
UncompressedSize = subChunk.UncompressedSize,
LodIdx = i,
SubChunkIdx = j
};
datBlock.Read(bs);
datBlocks.Add(datBlock);
LodDataMap[i].DatBlockIds = datBlockLodMappings.ToArray();
}
}
DatBlocks = datBlocks.ToArray();
}
private Node ReadNodeHeader(BinaryStream bs)
{
var nodeHeader = new Node(bs);
return nodeHeader;
}
public override void Write(BinaryStream bs)
{
base.Write(bs);
}
#region Types
public enum NodeTypes : int
{
// Structure nodes
Root = 262144,
LOD = 262145,
SubChunk = 262146,
// Compressed block nodes
TerrainChunk = 262400,
GeoData = 262401,
GeoData2 = 262405,
GeoData3 = 262403,
PropEncNameReg = 262660,
VegationChunk = 262661,
VegationChunk2 = 262665,
OverlayChunk = 262662,
SectorsChunk = 262663,
WaterObjectChunk = 262664,
PropChunk = 262656,
SubZoneGrid = 262402,
}
// casting and boxing yay, but can revise later if its really an issue in how it ends up getting used
public struct NodeDataWrapper
{
public uint NodeId;
public object NodeData;
public NodeDataWrapper(uint nodeType, object obj)
{
NodeId = nodeType;
NodeData = obj;
}
public NodeTypes NodeType => (NodeTypes) NodeId;
public GtChunk_MeshData AsMeshData => NodeData as GtChunk_MeshData;
}
// Mapp an lod idx to compressed blocks
public struct LodDataMapping
{
public int DataBlockIdx;
public short[] DatBlockIds;
}
// the uncompressed byte array for an lod and its sub chunks
public class LodSubChunkData
{
public byte[] LodData;
public byte[][] SubChunkData;
}
public class Node : ReadWrite
{
public ulong NodeMarker;
public uint NodeId;
public int Length;
public NodeTypes NodeType => (NodeTypes) NodeId;
public Node(BinaryStream bs)
{
Read(bs);
}
public Node()
{
}
public void Read(BinaryStream bs)
{
NodeMarker = bs.Read.ULong();
NodeId = bs.Read.UInt();
Length = bs.Read.Int();
}
public void Write(BinaryStream bs)
{
bs.Write.ULong(NODE_MARKER);
bs.Write.UInt(NodeId);
bs.Write.Int(Length);
}
}
public class RootNode : Node
{
public uint Version;
public ulong Timestamp;
public uint NumLods;
public LodNode[] LodNodes;
public new void Read(BinaryStream bs)
{
base.Read(bs);
Version = bs.Read.UInt();
Timestamp = bs.Read.ULong();
NumLods = bs.Read.UInt();
LodNodes = new LodNode[NumLods];
for (int i = 0; i < NumLods; i++) {
var lodNode = new LodNode();
lodNode.Read(bs);
LodNodes[i] = lodNode;
}
}
public new void Write(BinaryStream bs)
{
base.Write(bs);
bs.Write.UInt(Version);
bs.Write.ULong(Timestamp);
bs.Write.UInt(NumLods);
for (int i = 0; i < NumLods; i++) {
LodNodes[i].Write(bs);
}
}
}
public class LodNode : Node
{
public uint LodIdx;
public uint NumSubchunks;
public uint UNK1;
public int CompressedSize;
public int UncompressedSize;
public SubChunkNode[] SubChunkNodes;
public new void Read(BinaryStream bs)
{
base.Read(bs);
LodIdx = bs.Read.UInt();
NumSubchunks = (uint) (1 << 2 * bs.Read.Int());
UNK1 = bs.Read.UInt();
CompressedSize = bs.Read.Int();
UncompressedSize = bs.Read.Int();
// The chunks
SubChunkNodes = new SubChunkNode[NumSubchunks];
for (int i = 0; i < NumSubchunks; i++) {
var subChunk = new SubChunkNode();
subChunk.Read(bs);
SubChunkNodes[i] = subChunk;
}
}
public new void Write(BinaryStream bs)
{
base.Write(bs);
bs.Write.UInt(LodIdx);
bs.Write.UInt((uint) (1 >> (2 * (int) NumSubchunks)));
bs.Write.UInt(UNK1);
bs.Write.Int(UncompressedSize);
bs.Write.Int(CompressedSize);
for (int i = 0; i < NumSubchunks; i++) {
SubChunkNodes[i].Write(bs);
}
}
}
public class SubChunkNode : Node
{
public uint UNK1;
public int CompressedSize;
public int UncompressedSize;
public Vector3 BoundsMin;
public Vector3 BoundsMax;
public new void Read(BinaryStream bs)
{
base.Read(bs);
UNK1 = bs.Read.UInt();
CompressedSize = bs.Read.Int();
UncompressedSize = bs.Read.Int();
BoundsMax = bs.Read.Vector3();
BoundsMin = bs.Read.Vector3();
}
public new void Write(BinaryStream bs)
{
base.Write(bs);
bs.Write.UInt(UNK1);
bs.Write.Int(CompressedSize);
bs.Write.Int(UncompressedSize);
bs.Write.Vector3(BoundsMax);
bs.Write.Vector3(BoundsMin);
}
}
// A compressed block of memory
public class DataBlock : ReadWrite
{
private const int DATA_ID = 0x41544144;
public int Unk1;
public byte[] CompressedData;
public int CompressedSize;
public int UncompressedSize;
public int LodIdx;
public void Read(BinaryStream bs)
{
var id = bs.Read.UInt();
if (id != DATA_ID) throw new Exception("Didn't get a DATA id");
//Unk1 = bs.Read.Int();
//bs.Read.ByteArray(5 * 4);
CompressedData = bs.Read.ByteArray(CompressedSize);
}
public void Write(BinaryStream bs)
{
bs.Write.UInt(DATA_ID);
//bs.Write.Int(Unk1);
bs.Write.ByteArray(CompressedData);
}
}
// LZMA compressed subchunk data
public class DatBlock : ReadWrite
{
private const int DAT_ID = 0x32544144;
public byte[] Properites;
public byte[] CompressedData;
public int CompressedSize;
public int UncompressedSize;
public int LodIdx;
public int SubChunkIdx;
// Decompressed the data in this block
public byte[] Decompress()
{
var decompressed = new byte[UncompressedSize];
using var lzmaStream = new LzmaStream(Properites, new MemoryStream(CompressedData));
lzmaStream.Read(decompressed, 0, decompressed.Length);
return decompressed;
}
public void Read(BinaryStream bs)
{
var id = bs.Read.UInt();
if (id != DAT_ID) throw new Exception("Didn't get a DAT id");
Properites = bs.Read.ByteArray(5);
CompressedData = bs.Read.ByteArray(CompressedSize);
}
public void Write(BinaryStream bs)
{
bs.Write.UInt(DAT_ID);
//bs.Write.Int(Unk1);
bs.Write.ByteArray(CompressedData);
}
}
#endregion
}
}