aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadia Holmquist Pedersen <nadia@nhp.sh>2023-07-14 03:02:06 +0200
committerNadia Holmquist Pedersen <nadia@nhp.sh>2023-07-14 03:05:34 +0200
commitf432e559d41017f668009f438cec6d9be1dcab44 (patch)
tree8705822f6da7f73f0f11df0fa23163ba7242aa83
parentca5e8792c826774d7769ab18de4bc5186134545f (diff)
Add a fallback to streaming decompression when loading zstd-compressed ROMs.
Because of course some compression programs aren't nice enough to tell you the decompressed size up front in the file, so the other approach will fail. Things just can't ever be easy and straight forward, can they?
-rw-r--r--src/frontend/qt_sdl/ROMManager.cpp83
1 files changed, 74 insertions, 9 deletions
diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp
index 5aa6f59..a3a911c 100644
--- a/src/frontend/qt_sdl/ROMManager.cpp
+++ b/src/frontend/qt_sdl/ROMManager.cpp
@@ -550,23 +550,88 @@ bool LoadBIOS()
u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent)
{
u64 realSize = ZSTD_getFrameContentSize(inContent, inSize);
+ const u32 maxSize = 0x40000000;
- if (realSize == ZSTD_CONTENTSIZE_UNKNOWN || realSize == ZSTD_CONTENTSIZE_ERROR || realSize > 0x40000000)
+ if (realSize == ZSTD_CONTENTSIZE_ERROR || (realSize > maxSize && realSize != ZSTD_CONTENTSIZE_UNKNOWN))
{
return 0;
}
- u8* realContent = new u8[realSize];
- u64 decompressed = ZSTD_decompress(realContent, realSize, inContent, inSize);
-
- if (ZSTD_isError(decompressed))
+ if (realSize != ZSTD_CONTENTSIZE_UNKNOWN)
{
- delete[] realContent;
- return 0;
+ u8* realContent = new u8[realSize];
+ u64 decompressed = ZSTD_decompress(realContent, realSize, inContent, inSize);
+
+ if (ZSTD_isError(decompressed))
+ {
+ delete[] realContent;
+ return 0;
+ }
+
+ *outContent = realContent;
+ return realSize;
}
+ else
+ {
+ ZSTD_DStream* dStream = ZSTD_createDStream();
+ ZSTD_initDStream(dStream);
+
+ ZSTD_inBuffer inBuf = {
+ .src = inContent,
+ .size = inSize,
+ .pos = 0
+ };
+
+ const u32 startSize = 1024 * 1024 * 16;
+ u8* partialOutContent = (u8*) malloc(startSize);
+
+ ZSTD_outBuffer outBuf = {
+ .dst = partialOutContent,
+ .size = startSize,
+ .pos = 0
+ };
+
+ size_t result;
+
+ do
+ {
+ result = ZSTD_decompressStream(dStream, &outBuf, &inBuf);
- *outContent = realContent;
- return realSize;
+ if (ZSTD_isError(result))
+ {
+ ZSTD_freeDStream(dStream);
+ free(outBuf.dst);
+ return 0;
+ }
+
+ // if result == 0 and not inBuf.pos < inBuf.size, go again to let zstd flush everything.
+ if (result == 0)
+ continue;
+
+ if (outBuf.pos == outBuf.size)
+ {
+ outBuf.size *= 2;
+
+ if (outBuf.size > maxSize)
+ {
+ ZSTD_freeDStream(dStream);
+ free(outBuf.dst);
+ return 0;
+ }
+
+ outBuf.dst = realloc(outBuf.dst, outBuf.size);
+ }
+ } while (inBuf.pos < inBuf.size);
+
+ ZSTD_freeDStream(dStream);
+ *outContent = new u8[outBuf.pos];
+ memcpy(*outContent, outBuf.dst, outBuf.pos);
+
+ ZSTD_freeDStream(dStream);
+ free(outBuf.dst);
+
+ return outBuf.size;
+ }
}
void ClearBackupState()