Add an API to get the model from the state file

This commit is contained in:
Lior Halphon 2023-01-15 19:15:45 +02:00
parent a838c31d02
commit 0549dc702c
4 changed files with 150 additions and 39 deletions

View File

@ -6,6 +6,7 @@
#include <stdalign.h>
#include <time.h>
#include "model.h"
#include "defs.h"
#include "save_state.h"
@ -29,14 +30,6 @@
#define GB_STRUCT_VERSION 14
#define GB_MODEL_FAMILY_MASK 0xF00
#define GB_MODEL_DMG_FAMILY 0x000
#define GB_MODEL_MGB_FAMILY 0x100
#define GB_MODEL_CGB_FAMILY 0x200
#define GB_MODEL_GBP_BIT 0x20
#define GB_MODEL_PAL_BIT 0x40
#define GB_MODEL_NO_SFC_BIT 0x80
#define GB_REWIND_FRAMES_PER_KEY 255
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
@ -96,36 +89,6 @@ typedef struct __attribute__((packed)) {
uint8_t alarm_enabled;
} GB_huc3_rtc_time_t;
typedef enum {
// GB_MODEL_DMG_0 = 0x000,
// GB_MODEL_DMG_A = 0x001,
GB_MODEL_DMG_B = 0x002,
// GB_MODEL_DMG_C = 0x003,
GB_MODEL_SGB = 0x004,
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT,
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
GB_MODEL_MGB = 0x100,
GB_MODEL_SGB2 = 0x101,
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
GB_MODEL_CGB_0 = 0x200,
GB_MODEL_CGB_A = 0x201,
GB_MODEL_CGB_B = 0x202,
GB_MODEL_CGB_C = 0x203,
GB_MODEL_CGB_D = 0x204,
GB_MODEL_CGB_E = 0x205,
// GB_MODEL_AGB_0 = 0x206,
GB_MODEL_AGB_A = 0x207,
GB_MODEL_GBP_A = GB_MODEL_AGB_A | GB_MODEL_GBP_BIT, // AGB-A inside a Game Boy Player
GB_MODEL_AGB = GB_MODEL_AGB_A,
GB_MODEL_GBP = GB_MODEL_GBP_A,
//GB_MODEL_AGB_B = 0x208
//GB_MODEL_AGB_E = 0x209
//GB_MODEL_GBP_E = GB_MODEL_AGB_E | GB_MODEL_GBP_BIT, // AGB-E inside a Game Boy Player
} GB_model_t;
enum {
GB_REGISTER_AF,
GB_REGISTER_BC,

43
Core/model.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef model_h
#define model_h
#define GB_MODEL_FAMILY_MASK 0xF00
#define GB_MODEL_DMG_FAMILY 0x000
#define GB_MODEL_MGB_FAMILY 0x100
#define GB_MODEL_CGB_FAMILY 0x200
#define GB_MODEL_GBP_BIT 0x20
#define GB_MODEL_PAL_BIT 0x40
#define GB_MODEL_NO_SFC_BIT 0x80
typedef enum {
// GB_MODEL_DMG_0 = 0x000,
// GB_MODEL_DMG_A = 0x001,
GB_MODEL_DMG_B = 0x002,
// GB_MODEL_DMG_C = 0x003,
GB_MODEL_SGB = 0x004,
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT,
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
GB_MODEL_MGB = 0x100,
GB_MODEL_SGB2 = 0x101,
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
GB_MODEL_CGB_0 = 0x200,
GB_MODEL_CGB_A = 0x201,
GB_MODEL_CGB_B = 0x202,
GB_MODEL_CGB_C = 0x203,
GB_MODEL_CGB_D = 0x204,
GB_MODEL_CGB_E = 0x205,
// GB_MODEL_AGB_0 = 0x206,
GB_MODEL_AGB_A = 0x207,
GB_MODEL_GBP_A = GB_MODEL_AGB_A | GB_MODEL_GBP_BIT, // AGB-A inside a Game Boy Player
GB_MODEL_AGB = GB_MODEL_AGB_A,
GB_MODEL_GBP = GB_MODEL_GBP_A,
//GB_MODEL_AGB_B = 0x208
//GB_MODEL_AGB_E = 0x209
//GB_MODEL_GBP_E = GB_MODEL_AGB_E | GB_MODEL_GBP_BIT, // AGB-E inside a Game Boy Player
} GB_model_t;
#endif

View File

@ -1300,7 +1300,6 @@ static int load_state_internal(GB_gameboy_t *gb, virtual_file_t *file)
if (!READ_SECTION(&save, file, apu )) return errno ?: EIO;
if (!READ_SECTION(&save, file, rtc )) return errno ?: EIO;
if (!READ_SECTION(&save, file, video )) return errno ?: EIO;
#undef READ_SECTION
bool attempt_bess = false;
@ -1372,6 +1371,109 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
return load_state_internal(gb, &file);
}
static int get_state_model_bess(virtual_file_t *file, GB_model_t *model)
{
file->seek(file, -sizeof(BESS_footer_t), SEEK_END);
BESS_footer_t footer = {0, };
file->read(file, &footer, sizeof(footer));
if (footer.magic != BE32('BESS')) {
return -1;
}
file->seek(file, LE32(footer.start_offset), SEEK_SET);
while (true) {
BESS_block_t block;
if (file->read(file, &block, sizeof(block)) != sizeof(block)) return errno;
switch (block.magic) {
case BE32('CORE'): {
BESS_CORE_t core = {0,};
if (LE32(block.size) > sizeof(core) - sizeof(block)) {
if (file->read(file, &core.header + 1, sizeof(core) - sizeof(block)) != sizeof(core) - sizeof(block)) return errno;
file->seek(file, LE32(block.size) - (sizeof(core) - sizeof(block)), SEEK_CUR);
}
else {
if (file->read(file, &core.header + 1, LE32(block.size)) != LE32(block.size)) return errno;
}
switch (core.full_model) {
case BE32('GDB '): *model = GB_MODEL_DMG_B; return 0;
case BE32('GM '): *model = GB_MODEL_MGB; return 0;
case BE32('SN '): *model = GB_MODEL_SGB_NTSC_NO_SFC; return 0;
case BE32('SP '): *model = GB_MODEL_SGB_PAL; return 0;
case BE32('S2 '): *model = GB_MODEL_SGB2; return 0;
case BE32('CC0 '): *model = GB_MODEL_CGB_0; return 0;
case BE32('CCA '): *model = GB_MODEL_CGB_A; return 0;
case BE32('CCB '): *model = GB_MODEL_CGB_B; return 0;
case BE32('CCC '): *model = GB_MODEL_CGB_C; return 0;
case BE32('CCD '): *model = GB_MODEL_CGB_D; return 0;
case BE32('CCE '): *model = GB_MODEL_CGB_E; return 0;
case BE32('CAA '): *model = GB_MODEL_AGB_A; return 0;
}
return -1;
default:
file->seek(file, LE32(block.size), SEEK_CUR);
break;
}
}
}
return -1;
}
static int get_state_model_internal(virtual_file_t *file, GB_model_t *model)
{
GB_gameboy_t save;
bool fix_broken_windows_saves = false;
if (file->read(file, GB_GET_SECTION(&save, header), GB_SECTION_SIZE(header)) != GB_SECTION_SIZE(header)) return errno;
if (save.magic == 0) {
/* Potentially legacy, broken Windows save state*/
file->seek(file, 4, SEEK_SET);
if (file->read(file, GB_GET_SECTION(&save, header), GB_SECTION_SIZE(header)) != GB_SECTION_SIZE(header)) return errno;
fix_broken_windows_saves = true;
}
if (save.magic != state_magic()) {
return get_state_model_bess(file, model);
}
if (!READ_SECTION(&save, file, core_state)) return errno ?: EIO;
*model = save.model;
return 0;
}
int GB_get_state_model(const char *path, GB_model_t *model)
{
FILE *f = fopen(path, "rb");
if (!f) {
return errno;
}
virtual_file_t file = {
.read = file_read,
.seek = file_seek,
.tell = file_tell,
.file = f,
};
int ret = get_state_model_internal(&file, model);
fclose(f);
return ret;
}
int GB_get_state_model_from_buffer(const uint8_t *buffer, size_t length, GB_model_t *model)
{
virtual_file_t file = {
.read = buffer_read,
.seek = buffer_seek,
.tell = buffer_tell,
.buffer = (uint8_t *)buffer,
.position = 0,
.size = length,
};
return get_state_model_internal(&file, model);
}
bool GB_is_save_state(const char *path)
{

View File

@ -33,6 +33,9 @@ void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer);
int GB_load_state(GB_gameboy_t *gb, const char *path);
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length);
bool GB_is_save_state(const char *path);
int GB_get_state_model(const char *path, GB_model_t *model);
int GB_get_state_model_from_buffer(const uint8_t *buffer, size_t length, GB_model_t *model);
#ifdef GB_INTERNAL
static inline uint32_t state_magic(void)
{