mirror of https://github.com/LIJI32/SameBoy.git
Add an API to get the model from the state file
This commit is contained in:
parent
a838c31d02
commit
0549dc702c
39
Core/gb.h
39
Core/gb.h
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue