ref: fbbb3f967a51fafe642e6140d0753979e73b4090
parent: 43db8cc0f529a2b8004a684526d91582d0d6d5d7
author: Snesrev <snesrev@protonmail.com>
date: Wed Aug 16 23:16:50 EDT 2023
Add BPS support
--- a/src/main.c
+++ b/src/main.c
@@ -811,9 +811,19 @@
static void LoadAssets() {
size_t length = 0;
uint8 *data = ReadWholeFile("zelda3_assets.dat", &length);
- if (!data)
- data = ReadWholeFile("tables/zelda3_assets.dat", &length);
- if (!data) Die("Failed to read zelda3_assets.dat. Please see the README for information about how you get this file.");
+ if (!data) {
+ size_t bps_length, bps_src_length;
+ uint8 *bps, *bps_src;
+ bps = ReadWholeFile("zelda3_assets.bps", &bps_length);
+ if (!bps)
+ Die("Failed to read zelda3_assets.dat. Please see the README for information about how you get this file.");
+ bps_src = ReadWholeFile("zelda3.sfc", &bps_src_length);
+ if (!bps_src)
+ Die("Missing file: zelda3.sfc");
+ data = ApplyBps(bps_src, bps_src_length, bps, bps_length, &length);
+ if (!data)
+ Die("Unable to apply zelda3_assets.bps. Please make sure you got the right version of 'zelda3.sfc'");
+ }
static const char kAssetsSig[] = { kAssets_Sig };
--- a/src/util.c
+++ b/src/util.c
@@ -193,3 +193,88 @@
return (MemBlk) { 0, 0 };
return (MemBlk) { data.ptr + left_off, right_off - left_off };
}
+
+
+static uint64 BpsDecodeInt(const uint8 **src) {
+ uint64 data = 0, shift = 1;
+ while(true) {
+ uint8 x = *(*src)++;
+ data += (x & 0x7f) * shift;
+ if(x & 0x80) break;
+ shift <<= 7;
+ data += shift;
+ }
+ return data;
+}
+
+#define CRC32_POLYNOMIAL 0xEDB88320
+
+static uint32 crc32(const void *data, size_t length) {
+ uint32 crc = 0xFFFFFFFF;
+ const uint8 *byteData = (const uint8 *)data;
+ for (size_t i = 0; i < length; i++) {
+ crc ^= byteData[i];
+ for (int j = 0; j < 8; j++)
+ crc = (crc >> 1) ^ ((crc & 1) * CRC32_POLYNOMIAL);
+ }
+ return crc ^ 0xFFFFFFFF;
+}
+
+uint8 *ApplyBps(const uint8 *src, size_t src_size_in,
+ const uint8 *bps, size_t bps_size, size_t *length_out) {
+ const uint8 *bps_end = bps + bps_size - 12;
+
+ if (memcmp(bps, "BPS1", 4))
+ return NULL;
+ if (crc32(src, src_size_in) != *(uint32 *)(bps_end))
+ return NULL;
+ if (crc32(bps, bps_size - 4) != *(uint32 *)(bps_end + 8))
+ return NULL;
+
+ bps += 4;
+ uint32 src_size = BpsDecodeInt(&bps);
+ uint32 dst_size = BpsDecodeInt(&bps);
+ uint32 meta_size = BpsDecodeInt(&bps);
+ uint32 outputOffset = 0;
+ uint32 sourceRelativeOffset = 0;
+ uint32 targetRelativeOffset = 0;
+ if (src_size != src_size_in)
+ return NULL;
+ *length_out = dst_size;
+ uint8 *dst = malloc(dst_size);
+ if (!dst)
+ return NULL;
+ while (bps < bps_end) {
+ uint32 cmd = BpsDecodeInt(&bps);
+ uint32 length = (cmd >> 2) + 1;
+ switch (cmd & 3) {
+ case 0:
+ while(length--) {
+ dst[outputOffset] = src[outputOffset];
+ outputOffset++;
+ }
+ break;
+ case 1:
+ while (length--)
+ dst[outputOffset++] = *bps++;
+ break;
+ case 2:
+ cmd = BpsDecodeInt(&bps);
+ sourceRelativeOffset += (cmd & 1 ? -1 : +1) * (cmd >> 1);
+ while (length--)
+ dst[outputOffset++] = src[sourceRelativeOffset++];
+ break;
+ default:
+ cmd = BpsDecodeInt(&bps);
+ targetRelativeOffset += (cmd & 1 ? -1 : +1) * (cmd >> 1);
+ while(length--)
+ dst[outputOffset++] = dst[targetRelativeOffset++];
+ break;
+ }
+ }
+ if (dst_size != outputOffset)
+ return NULL;
+ if (crc32(dst, dst_size) != *(uint32 *)(bps_end + 4))
+ return NULL;
+ return dst;
+}
\ No newline at end of file
--- a/src/util.h
+++ b/src/util.h
@@ -35,5 +35,7 @@
void StrSet(char **rv, const char *s);
char *StrFmt(const char *fmt, ...);
char *ReplaceFilenameWithNewPath(const char *old_path, const char *new_path);
+uint8 *ApplyBps(const uint8 *src, size_t src_size_in,
+ const uint8 *bps, size_t bps_size, size_t *length_out);
#endif // ZELDA3_UTIL_H_
\ No newline at end of file
--- a/zelda3.vcxproj
+++ b/zelda3.vcxproj
@@ -202,6 +202,7 @@
<TreatWarningAsError>true</TreatWarningAsError>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>