From f0a648854652b7cce4a4c959de9dd508c72b4a62 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Thu, 15 Apr 2021 21:57:38 +0300 Subject: [PATCH] Added optional INFO block --- BESS.md | 19 ++++++++++++++----- Core/save_state.c | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/BESS.md b/BESS.md index 9a4a10f..bb49e2a 100644 --- a/BESS.md +++ b/BESS.md @@ -37,14 +37,23 @@ The NAME block uses the `'NAME'` identifier, and is an optional block that conta The length of the NAME block is variable, and it only contains the name and version of the originating emulator in ASCII. +#### INFO block + +The INFO block uses the `'INFO'` identifier, and is an optional block that contains information about the ROM this save state originates from. When used, this block should come before `CORE` but after `NAME`. This block is 0x12 bytes long, and it follows this structure: + +| Offset | Content | +|--------|--------------------------------------------------| +| 0x00 | Bytes 0x134-0x143 from the ROM (Title) | +| 0x10 | Bytes 0x14E-0x14F from the ROM (Global checksum) | + #### CORE block -The CORE block uses the `'CORE'` identifier, and is a required block that contains both core state information, as well as basic information about the BESS version used. This block must be the first block, unless the `NAME` block exists then it must be second. An implementation should not enforce block order on blocks unknown to it for future compatibility. +The CORE block uses the `'CORE'` identifier, and is a required block that contains both core state information, as well as basic information about the BESS version used. This block must be the first block, unless the `NAME` or `INFO` blocks exist then it must come directly after them. An implementation should not enforce block order on blocks unknown to it for future compatibility. The length of the CORE block is 0xD0 bytes, but implementations are expected to ignore any excess bytes. Following the BESS block header, the structure is as follows: -| Offset | Content | -|--------|---------------------------------------| +| Offset | Content | +|--------|----------------------------------------| | 0x00 | Major BESS version as a 16-bit integer | | 0x02 | Minor BESS version as a 16-bit integer | @@ -97,11 +106,11 @@ The values of memory-mapped registers should be written 'as-is' to memory as if | 0x9C | The offset of RAM from file start (32-bit integer) | | 0xA0 | The size of VRAM (32-bit integer) | | 0xA4 | The offset of VRAM from file start (32-bit integer) | -| 0xA9 | The size of MBC RAM (32-bit integer) | +| 0xA8 | The size of MBC RAM (32-bit integer) | | 0xAC | The offset of MBC RAM from file start (32-bit integer) | | 0xB0 | The size of OAM (=0xA0, 32-bit integer) | | 0xB4 | The offset of OAM from file start (32-bit integer) | -| 0xB9 | The size of HRAM (=0x7F, 32-bit integer) | +| 0xB8 | The size of HRAM (=0x7F, 32-bit integer) | | 0xBC | The offset of HRAM from file start (32-bit integer) | | 0xC0 | The size of background palettes (=0x40 or 0, 32-bit integer) | | 0xC4 | The offset of background palettes from file start (32-bit integer) | diff --git a/Core/save_state.c b/Core/save_state.c index 980b791..4516daa 100644 --- a/Core/save_state.c +++ b/Core/save_state.c @@ -82,6 +82,12 @@ typedef struct __attribute__((packed)) { uint8_t multiplayer_state; } BESS_SGB_t; +typedef struct __attribute__((packed)){ + BESS_block_t header; + char title[0x10]; + uint8_t checksum[2]; +} BESS_INFO_t; + /* Same RTC format as used by VBA, BGB and SameBoy in battery saves*/ typedef struct __attribute__((packed)){ BESS_block_t header; @@ -241,9 +247,10 @@ size_t GB_get_save_state_size(GB_gameboy_t *gb) { return GB_get_save_state_size_no_bess(gb) + // BESS - + sizeof(BESS_CORE_t) + sizeof(BESS_block_t) // NAME + sizeof(BESS_NAME) - 1 + + sizeof(BESS_INFO_t) // INFO + + sizeof(BESS_CORE_t) + sizeof(BESS_XOAM_t) + (gb->sgb? sizeof(BESS_SGB_t) : 0) + bess_size_for_cartridge(gb->cartridge_type) // MBC & RTC/HUC3 block @@ -530,6 +537,22 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file, bool appe goto error; } + /* BESS INFO */ + + static const BESS_block_t bess_info = {BE32('INFO'), LE32(sizeof(BESS_INFO_t) - sizeof(BESS_block_t))}; + + if (file->write(file, &bess_info, sizeof(bess_info)) != sizeof(bess_info)) { + goto error; + } + + if (file->write(file, gb->rom + 0x134, 0x10) != 0x10) { + goto error; + } + + if (file->write(file, gb->rom + 0x14e, 2) != 2) { + goto error; + } + /* BESS CORE */ bess_core.header = (BESS_block_t){BE32('CORE'), LE32(sizeof(bess_core) - sizeof(bess_core.header))}; @@ -936,6 +959,23 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo file->read(file, emulator_name, LE32(block.size)); } break; + case BE32('INFO'): { + BESS_INFO_t bess_info = {0,}; + if (LE32(block.size) != sizeof(bess_info) - sizeof(block)) goto parse_error; + if (file->read(file, &bess_info.header + 1, LE32(block.size)) != LE32(block.size)) goto error; + if (memcmp(bess_info.title, gb->rom + 0x134, sizeof(bess_info.title))) { + char ascii_title[0x11] = {0,}; + for (unsigned i = 0; i < 0x10; i++) { + if (bess_info.title[i] < 0x20 || bess_info.title[i] > 0x7E) break; + ascii_title[i] = bess_info.title[i]; + } + GB_log(gb, "Save state was made on another ROM: '%s'\n", ascii_title); + } + else if (memcmp(bess_info.checksum, gb->rom + 0x14E, 2)) { + GB_log(gb, "Save state was potentially made on another revision of the same ROM.\n"); + } + break; + } case BE32('XOAM'): if (!found_core) goto parse_error; if (LE32(block.size) != 96) goto parse_error;