From c618cd595ce75be627126751ba2781b495140d1c Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Sat, 2 Dec 2017 15:45:13 -0500 Subject: [PATCH] unscramble bootfile of CD-ROM XA discs during HLE bootstrap --- CMakeLists.txt | 1 + src/guest/bios/bios.c | 15 +++++++- src/guest/bios/scramble.c | 72 +++++++++++++++++++++++++++++++++++++++ src/guest/bios/scramble.h | 8 +++++ src/guest/bios/syscalls.c | 6 +++- 5 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 src/guest/bios/scramble.c create mode 100644 src/guest/bios/scramble.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b0c0849..92cb8902 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -207,6 +207,7 @@ set(RELIB_SOURCES src/guest/arm7/arm7.c src/guest/bios/bios.c src/guest/bios/flash.c + src/guest/bios/scramble.c src/guest/bios/syscalls.c src/guest/gdrom/cdi.c src/guest/gdrom/chd.c diff --git a/src/guest/bios/bios.c b/src/guest/bios/bios.c index 33a9c7d7..5f56b906 100644 --- a/src/guest/bios/bios.c +++ b/src/guest/bios/bios.c @@ -4,6 +4,7 @@ #include "guest/aica/aica.h" #include "guest/bios/bios.h" #include "guest/bios/flash.h" +#include "guest/bios/scramble.h" #include "guest/bios/syscalls.h" #include "guest/dreamcast.h" #include "guest/gdrom/gdrom.h" @@ -285,6 +286,18 @@ void bios_boot(struct bios *bios) { return; } + /* CD-ROM XA discs have their binary scrambled. the bios descrambles this + later on in the boot, but it should be fine to descramble now */ + struct gd_status_info stat; + gdrom_get_status(gd, &stat); + + if (stat.format == GD_DISC_CDROM_XA) { + uint8_t *tmp2 = malloc(len); + descramble(tmp2, tmp, len); + free(tmp); + tmp = tmp2; + } + sh4_memcpy_to_guest(dc->mem, BOOT2_ADDR, tmp, read); free(tmp); } @@ -319,7 +332,7 @@ void bios_boot(struct bios *bios) { sh4_write32(dc->mem, VECTOR_SYSTEM, SYSCALL_SYSTEM); } - /* start executing at license screen code inside of ip.bin */ + /* start executing at license screen code inside of IP.BIN */ ctx->pc = 0xac008300; } diff --git a/src/guest/bios/scramble.c b/src/guest/bios/scramble.c new file mode 100644 index 00000000..ac5f2600 --- /dev/null +++ b/src/guest/bios/scramble.c @@ -0,0 +1,72 @@ +#include "guest/bios/scramble.h" +#include "core/core.h" + +#define MIN_CHUNK_SIZE 32 +#define MAX_CHUNK_SIZE (2048 * 1024) + +#define MAX_SLICES (MAX_CHUNK_SIZE / MIN_CHUNK_SIZE) + +static int scramble_init(int n) { + return n & 0xffff; +} + +static int scramble_next(int *seed, int i) { + unsigned key; + *seed = (*seed * 2109 + 9273) & 0x7fff; + key = (*seed + 0xc000) & 0xffff; + return ((unsigned)i * (unsigned)key) >> 16; +} + +static void descramble_chunk(int *seed, uint8_t *dst, const uint8_t *src, + int size) { + CHECK((size % MIN_CHUNK_SIZE) == 0 && size <= MAX_CHUNK_SIZE); + + /* descramble each chunk in MIN_CHUNK_SIZE slices */ + size /= MIN_CHUNK_SIZE; + + /* lookup table maps scrambled slice index to descrambled index */ + int table[MAX_SLICES]; + + for (int i = 0; i < size; i++) { + table[i] = i; + } + + for (int i = size - 1; i >= 0; i--) { + int x = scramble_next(seed, i); + + /* swap table index */ + int tmp = table[i]; + table[i] = table[x]; + table[x] = tmp; + + /* write slice out to descrambled index */ + memcpy(dst + MIN_CHUNK_SIZE * table[i], src, MIN_CHUNK_SIZE); + src += MIN_CHUNK_SIZE; + } +} + +void descramble(uint8_t *dst, const uint8_t *src, int size) { + int seed = scramble_init(size); + + /* descramble the data starting with the largest chunk size (2mb) */ + int chunk_size = MAX_CHUNK_SIZE; + + while (chunk_size >= MIN_CHUNK_SIZE) { + /* continue descrambling with the current chunk size until the remaining + data is too small */ + while (size >= chunk_size) { + descramble_chunk(&seed, dst, src, chunk_size); + size -= chunk_size; + dst += chunk_size; + src += chunk_size; + } + + /* attempt to use the the next smallest chunk size */ + chunk_size >>= 1; + } + + /* any remaining data isn't scrambled, just copy it */ + if (size) { + memcpy(dst, src, size); + } +} diff --git a/src/guest/bios/scramble.h b/src/guest/bios/scramble.h new file mode 100644 index 00000000..d7ad6108 --- /dev/null +++ b/src/guest/bios/scramble.h @@ -0,0 +1,8 @@ +#ifndef SCRAMBLE_H +#define SCRAMBLE_H + +#include + +void descramble(uint8_t *dst, const uint8_t *src, int size); + +#endif diff --git a/src/guest/bios/syscalls.c b/src/guest/bios/syscalls.c index e7a0ee66..965f6e06 100644 --- a/src/guest/bios/syscalls.c +++ b/src/guest/bios/syscalls.c @@ -1,6 +1,7 @@ #include "guest/bios/syscalls.h" #include "guest/bios/bios.h" #include "guest/bios/flash.h" +#include "guest/bios/scramble.h" #include "guest/gdrom/gdrom.h" #include "guest/holly/holly.h" #include "guest/memory.h" @@ -29,6 +30,7 @@ enum { void bios_system_vector(struct bios *bios) { struct dreamcast *dc = bios->dc; + struct gdrom *gd = dc->gdrom; struct sh4_context *ctx = &dc->sh4->ctx; uint32_t fn = ctx->r[4]; @@ -138,6 +140,7 @@ static uint32_t bios_gdrom_send_cmd(struct bios *bios, uint32_t cmd_code, static void bios_gdrom_mainloop(struct bios *bios) { struct dreamcast *dc = bios->dc; struct gdrom *gd = dc->gdrom; + struct holly *hl = dc->holly; if (bios->status != GDC_STATUS_ACTIVE) { return; @@ -265,7 +268,8 @@ static void bios_gdrom_mainloop(struct bios *bios) { } break; case GDC_INIT: { - /* this seems to always immediately follow GDROM_INIT */ + /* sanity check in case dma transfers are made async in the future */ + CHECK_EQ(*hl->SB_GDST, 0); } break; case GDC_SEEK: {