1#include <filedevice/seadFileDeviceMgr.h>
2#include <heap/seadHeap.h>
3#include <heap/seadHeapMgr.h>
4#include <math/seadMathCalcCommon.h>
5#include <prim/seadBitUtil.h>
6#include <prim/seadEndian.h>
7#include <prim/seadPtrUtil.h>
8#include <prim/seadSafeString.h>
9#include <resource/seadSZSDecompressor.h>
10
11namespace
12{
13#ifdef cafe
14__attribute__((aligned(0x20))) s32 decodeSZSCafeAsm_(void* dst, const void* src)
15{
16 asm("lwz r5, 0x4(r4)\n");
17 asm("li r11, 0x20\n");
18 asm("li r6, 0\n");
19 asm("mr r0, r5\n");
20 asm("addi r4, r4, 0xf\n");
21 asm("subi r3, r3, 1\n");
22 asm("cmpwi r5, 0x132\n");
23 asm("ble _final_decloop0\n");
24 asm("subi r5, r5, 0x132\n");
25 asm("nop\n");
26 asm("nop\n");
27 asm("nop\n");
28 asm("nop\n");
29 asm("nop\n");
30 asm("nop\n");
31 asm("nop\n");
32
33 asm("_decloop0: rlwinm. r6, r6, 0x1f, 1, 0x1f\n");
34 asm("bne _decloop1\n");
35 asm("lbzu r7, 1(r4)\n");
36 asm("li r6, 0x80\n");
37
38 asm("_decloop1: and. r8, r6, r7\n");
39 asm("lbzu r8, 1(r4)\n");
40 asm("beq _decloop2\n");
41 asm("andi. r9, r3, 0x1f\n");
42 asm("bne _decloop1x\n");
43 asm("dcbz r11, r3\n");
44
45 asm("_decloop1x: subic. r5, r5, 1\n");
46 asm("stbu r8, 1(r3)\n");
47 asm("bne _decloop0\n");
48 asm("b _decloop8\n");
49
50 asm("_decloop2: lbzu r9, 1(r4)\n");
51 asm("rlwinm. r10, r8, 0x1c, 4, 0x1f\n");
52 asm("bne _decloop3\n");
53 asm("lbzu r10, 1(r4)\n");
54 asm("addi r10, r10, 0x10");
55
56 asm("_decloop3: addi r10, r10, 2\n");
57 asm("rlwimi r9, r8, 8, 0x14, 0x17\n");
58 asm("subf r5, r10, r5\n");
59 asm("subf r8, r9, r4\n");
60 asm("mtspr CTR, r10\n");
61 asm("addi r8, r8, 1\n");
62
63 asm("_decloop4: andi. r9, r3, 0x1f\n");
64 asm("lbz r9, -1(r8)\n");
65 asm("addi r8, r8, 1\n");
66 asm("bne _decloop5\n");
67 asm("dcbz r11, r3\n");
68
69 asm("_decloop5: stbu r9, 1(r3)\n");
70 asm("bdnz _decloop4\n");
71 asm("cmpwi r5, 0\n");
72 asm("bgt _decloop0\n");
73
74 asm("_decloop8: addi r5, r5, 0x132\n");
75 asm("cmpwi r5, 0\n");
76 asm("ble _final_decloop8\n");
77
78 asm("_final_decloop0: rlwinm. r6, r6, 0x1f, 1, 0x1f\n");
79 asm("bne _final_decloop1\n");
80 asm("lbzu r7, 1(r4)\n");
81 asm("li r6, 0x80\n");
82
83 asm("_final_decloop1: and. r8, r6, r7\n");
84 asm("lbzu r8, 1(r4)\n");
85 asm("beq _final_decloop2\n");
86 asm("subic. r5, r5, 1\n");
87 asm("stbu r8, 1(r3)\n");
88 asm("bne _final_decloop0\n");
89 asm("b _final_decloop8\n");
90
91 asm("_final_decloop2: lbzu r9, 1(r4)\n");
92 asm("rlwinm. r10, r8, 0x1c, 4, 0x1f\n");
93 asm("bne _final_decloop3\n");
94 asm("lbzu r10, 1(r4)\n");
95 asm("addi r10, r10, 0x10\n");
96
97 asm("_final_decloop3: addi r10, r10, 2\n");
98 asm("rlwimi r9, r8, 8, 0x14, 0x17\n");
99 asm("subf. r5, r10, r5\n");
100 asm("blt _final_decloop8\n");
101 asm("subf r8, r9, r3\n");
102 asm("mtspr CTR, r10\n");
103 asm("addi r8, r8, 1\n");
104
105 asm("_final_decloop4: lbz r9, -1(r8)\n");
106 asm("addi r8, r8, 1\n");
107 asm("stbu r9, 1(r3)\n");
108 asm("bdnz _final_decloop4\n");
109 asm("cmpwi r5, 0\n");
110 asm("bgt _final_decloop0\n");
111
112 s32 register error asm("r3");
113 asm("_final_decloop8: mr %0, r0\n" : "=r"(error));
114 asm("blr");
115
116 return error;
117}
118#endif // cafe
119} // namespace
120
121#ifdef SWITCH
122s32 decodeSZSNxAsm64_(void* dst, const void* src);
123#endif
124
125namespace sead
126{
127SZSDecompressor::DecompContext::DecompContext()
128{
129 initialize(NULL);
130}
131
132SZSDecompressor::DecompContext::DecompContext(void* dst)
133{
134 initialize(dst);
135}
136
137void SZSDecompressor::DecompContext::initialize(void* dst)
138{
139 destp = static_cast<u8*>(dst);
140 destCount = 0;
141 forceDestCount = 0;
142 flagMask = 0;
143 flags = 0;
144 packHigh = 0;
145 step = SZSDecompressor::cStepNormal;
146 lzOffset = 0;
147 headerSize = 0x10;
148}
149
150SZSDecompressor::SZSDecompressor(u32 workSize, u8* workBuffer) : Decompressor("szs")
151{
152 if (workBuffer == NULL)
153 {
154 mWorkSize = Mathu::roundUpPow2(val: workSize, base: FileDevice::cBufferMinAlignment);
155 mWorkBuffer = NULL;
156 }
157
158 else
159 {
160 mWorkSize = workSize;
161 mWorkBuffer = workBuffer;
162 }
163}
164
165u8* SZSDecompressor::tryDecompFromDevice(const ResourceMgr::LoadArg& loadArg, Resource* resource,
166 u32* outSize, u32* outAllocSize, bool* outAllocated)
167{
168 Heap* heap = loadArg.load_data_heap;
169 if (heap == NULL)
170 heap = HeapMgr::sInstancePtr->getCurrentHeap();
171
172 FileHandle handle;
173 FileDevice* device;
174 u8* src;
175
176 if (loadArg.device != NULL)
177 device = loadArg.device->tryOpen(handle: &handle, path: loadArg.path, flag: FileDevice::cFileOpenFlag_ReadOnly,
178 divSize: loadArg.div_size);
179 else
180 device = FileDeviceMgr::instance()->tryOpen(
181 handle: &handle, path: loadArg.path, flag: FileDevice::cFileOpenFlag_ReadOnly, divSize: loadArg.div_size);
182
183 if (device != NULL &&
184 ((src = mWorkBuffer, src != NULL) ||
185 (src = new (heap, -FileDevice::cBufferMinAlignment) u8[mWorkSize], src != NULL)))
186 {
187 u32 bytesRead = handle.read(outBuffer: src, bytesToRead: mWorkSize);
188 if (bytesRead >= 0x10)
189 {
190 u32 decompSize = getDecompSize(src);
191 s32 decompAlignment = getDecompAlignment(src);
192
193 u32 allocSize = loadArg.load_data_buffer_size;
194 u8* dst = loadArg.load_data_buffer;
195
196 if (decompSize > allocSize && allocSize != 0)
197 decompSize = allocSize;
198
199 bool allocated = false;
200 allocSize = Mathu::roundUpPow2(val: decompSize, base: 0x20);
201
202 if (dst == NULL)
203 {
204 DirectResource* directResource = DynamicCast<DirectResource, Resource>(obj: resource);
205 if (directResource != NULL)
206 {
207 s32 alignment = loadArg.load_data_alignment;
208 if (alignment != 0)
209 decompAlignment = (alignment < 0x20) ? 0x20 : alignment;
210
211 else
212 {
213 if (decompAlignment == 0)
214 decompAlignment = directResource->getLoadDataAlignment();
215
216 decompAlignment = ((loadArg.instance_alignment < 0) ? -1 : 1) *
217 ((decompAlignment < 0x20) ? 0x20 : decompAlignment);
218 }
219 }
220
221 else
222 decompAlignment = -(((loadArg.instance_alignment < 0) ? -1 : 1) << 5);
223
224 dst = new (heap, decompAlignment) u8[allocSize];
225
226 if (dst != NULL)
227 allocated = true;
228 }
229
230 if (dst != NULL)
231 {
232 s32 error;
233 if (bytesRead < mWorkSize)
234 error = decomp(dst, dstSize: allocSize, src, srcSize: mWorkSize);
235
236 else
237 {
238 DecompContext context(dst);
239 context.forceDestCount = decompSize;
240
241 do
242 {
243 error = streamDecomp(context: &context, src, srcSize: bytesRead);
244 if (error <= 0)
245 break;
246 } while ((bytesRead = handle.read(outBuffer: src, bytesToRead: mWorkSize), bytesRead != 0));
247 }
248
249 if (!(error < 0))
250 {
251 if (mWorkBuffer == NULL)
252 delete[] src;
253
254 if (outSize != NULL)
255 *outSize = decompSize;
256
257 if (outAllocSize != NULL)
258 *outAllocSize = allocSize;
259
260 if (outAllocated != NULL)
261 *outAllocated = allocated;
262
263 return dst;
264 }
265
266 if (allocated)
267 delete[] dst;
268 }
269 }
270
271 if (mWorkBuffer == NULL)
272 delete[] src;
273 }
274
275 return NULL;
276}
277
278u32 SZSDecompressor::getDecompAlignment(const void* src)
279{
280 return Endian::toHostU32(from: Endian::cBig, v: BitUtil::bitCastPtr<u32>(value: src, offset: 8));
281}
282
283u32 SZSDecompressor::getDecompSize(const void* src)
284{
285 return Endian::toHostU32(from: Endian::cBig, v: BitUtil::bitCastPtr<u32>(value: src, offset: 4));
286}
287
288s32 SZSDecompressor::readHeader_(DecompContext* context, const u8* src, u32 srcSize)
289{
290 s32 len = 0;
291
292 while (context->headerSize != 0)
293 {
294 context->headerSize -= 1;
295
296 if (context->headerSize == 0xF)
297 {
298 if (*src != 0x59)
299 return -1;
300 }
301
302 else if (context->headerSize == 0xE)
303 {
304 if (*src != 0x61)
305 return -1;
306 }
307
308 else if (context->headerSize == 0xD)
309 {
310 if (*src != 0x7A)
311 return -1;
312 }
313
314 else if (context->headerSize == 0xC)
315 {
316 if (*src != 0x30)
317 return -1;
318 }
319
320 else if (7 < context->headerSize)
321 context->destCount |= static_cast<u32>(*src) << (context->headerSize - 8) * 8;
322
323 src++;
324 len += 1;
325 if (--srcSize == 0 && context->headerSize != 0)
326 return len;
327 }
328
329 if (context->forceDestCount < 1)
330 return len;
331
332 if (context->destCount <= context->forceDestCount)
333 return len;
334
335 context->destCount = context->forceDestCount;
336 return len;
337}
338
339s32 SZSDecompressor::streamDecomp(DecompContext* context, const void* src, u32 srcSize)
340{
341 const u8* _src = static_cast<const u8*>(src);
342 u32 n;
343
344 if (context->headerSize != 0)
345 {
346 s32 len = readHeader_(context, src: _src, srcSize);
347 if (len < 0)
348 return len;
349
350 srcSize -= len;
351 _src += len;
352
353 if (srcSize == 0)
354 {
355 if (context->headerSize == 0)
356 return context->destCount;
357
358 return -1;
359 }
360 }
361
362 while (context->destCount > 0)
363 {
364 if (context->step == cStepLong)
365 {
366 n = *_src + 0x12;
367 if (!context->doCopy(n))
368 return -2;
369 }
370
371 else if (context->step == cStepShort)
372 {
373 context->lzOffset = (((context->packHigh << 8) & 0xf00) | *_src) + 1;
374
375 n = context->packHigh >> 4;
376 if (n != 0)
377 {
378 n += 2;
379 if (!context->doCopy(n))
380 return -2;
381 }
382
383 else
384 context->step = cStepLong;
385 }
386
387 else
388 {
389 if (context->flagMask == 0)
390 {
391 context->flags = *_src++;
392 context->flagMask = 0x80;
393 if (--srcSize == 0)
394 break;
395 }
396
397 if ((context->flags & context->flagMask) == 0)
398 {
399 context->packHigh = *_src;
400 context->step = cStepShort;
401 }
402
403 else
404 {
405 *context->destp++ = *_src;
406 context->destCount -= 1;
407 }
408
409 context->flagMask >>= 1;
410 }
411
412 if (--srcSize == 0)
413 break;
414
415 _src++;
416 }
417
418 if (context->destCount == 0 && context->forceDestCount == 0 && 0x20 < srcSize)
419 return -1;
420
421 else
422 return context->destCount;
423}
424
425s32 SZSDecompressor::decomp(void* dst, u32 dstSize, const void* src, u32)
426{
427 u32 magic = Endian::toHostU32(from: Endian::cBig, v: BitUtil::bitCastPtr<u32>(value: src));
428 if (magic != 0x59617A30)
429 return -1;
430
431 u32 decompSize = getDecompSize(src);
432 s32 error = -2;
433 if (dstSize >= decompSize)
434 {
435#ifdef cafe
436 error = decodeSZSCafeAsm_(dst, src);
437#elif defined(SWITCH)
438 error = decodeSZSNxAsm64_(dst, src);
439#else
440 SEAD_ASSERT_MSG(false, "SZSDecompressor::decomp not implemented");
441#endif // cafe
442 }
443
444 return error;
445}
446
447} // namespace sead
448