Skip to content

Commit

Permalink
Support bps patching
Browse files Browse the repository at this point in the history
  • Loading branch information
snesrev committed Aug 16, 2023
1 parent 747df5a commit eae20c6
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 13 deletions.
32 changes: 19 additions & 13 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -902,27 +902,33 @@ static const char *kAssetFileCandidates[] = {
};

static void LoadAssets() {
const char *verify_failed = NULL;

size_t length = 0;
uint8 *data = NULL;
for (int i = 0; i < 2 && data == NULL; i++) {
for (int i = 0; i < 2 && data == NULL; i++)
data = ReadWholeFile(kAssetFileCandidates[i], &length);
if (data && !VerifyAssetsFile(data, length)) {
verify_failed = kAssetFileCandidates[i];
free(data);
data = NULL;
}
}

if (!data) {
if (verify_failed)
Die("Invalid assets file - Please re run 'python assets/restool.py'");
else
size_t bps_length, bps_src_length;
uint8 *bps, *bps_src;

bps = ReadWholeFile("smw_assets.bps", &bps_length);
if (!bps)
Die("Failed to read smw_assets.dat. Please see the README for information about how you get this file.");

bps_src = ReadWholeFile("smw.sfc", &bps_src_length);
if (!bps_src)
Die("Missing file: smw.sfc");
if (bps_src_length != 524288)
Die("smw.sfc needs to be the unheadered ROM");

data = ApplyBps(bps_src, bps_src_length, bps, bps_length, &length);
if (!data)
Die("Unable to apply smw_assets.bps");
}


if (!VerifyAssetsFile(data, length))
Die("Mismatching assets file - Please re run 'python assets/restool.py'");

uint32 offset = 88 + kNumberOfAssets * 4 + *(uint32 *)(data + 84);

for (size_t i = 0; i < kNumberOfAssets; i++) {
Expand Down
85 changes: 85 additions & 0 deletions src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,88 @@ const uint8 *FindAddrInMemblk(MemBlk data, uint32 addr) {
return 0;
return data.ptr + offset;
}

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;
}
2 changes: 2 additions & 0 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,7 @@ const char *SkipPrefix(const char *big, const char *little);
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_

0 comments on commit eae20c6

Please sign in to comment.