|
- #include <inttypes.h>
- #include <string.h>
-
- #include "save.h"
- #include "file.h"
- #include "mapper.h"
- #include "filemap.h"
- #include "compat.h"
-
-
- /* SRAM */
-
- static int make_sram_filename(char* sram_filename, int max_len,
- const char* cart_filename) {
- return make_filename( sram_filename, max_len,
- basename(cart_filename),
- "sram", "sram");
- }
-
- int load_sram(nes_cart* cart, const char* cart_filename) {
- int status = -1;
-
- int sram_size = cart->mapper->sram_size ?
- cart->mapper->sram_size(cart->map_data) : 0;
- void* sram = cart->mapper->sram ?
- cart->mapper->sram(cart->map_data) : NULL;
-
- if (sram_size > 0 && NULL != sram) {
- char sram_filename[FILENAME_MAX] = {0};
- make_sram_filename(sram_filename, FILENAME_MAX - 1,
- cart_filename);
- status = read_file(sram_filename, sram, sram_size);
- }
-
- return status;
- }
-
- int save_sram(const nes_cart* cart, const char* cart_filename) {
- int status = -1;
-
- if (NULL != cart->mapper) {
- int sram_size = cart->mapper->sram_size ?
- cart->mapper->sram_size(cart->map_data) : 0;
- const void* sram = cart->mapper->sram ?
- cart->mapper->sram(cart->map_data) : NULL;
-
- if (sram_size > 0 && NULL != sram) {
- mkdir("sram");
- char sram_filename[FILENAME_MAX] = {0};
- make_sram_filename(sram_filename, FILENAME_MAX - 1,
- cart_filename);
- status = write_file(sram_filename, sram, sram_size);
- }
- }
-
- return status;
- }
-
-
- /* System State */
-
- static int make_state_filename(char* sram_filename, int max_len,
- const char* cart_filename) {
- return make_filename( sram_filename, max_len,
- basename(cart_filename),
- "save", "nese");
- }
-
- int load_state(nes* sys, const char* cart_filename) {
- int size = -1;
-
- char state_filename[FILENAME_MAX] = {0};
- make_state_filename(state_filename, FILENAME_MAX - 1,
- cart_filename);
-
- FILE* file = fopen(state_filename, "rb");
-
- if (NULL != file) {
- // int expected_size = state_size(sys);
-
- fseek(file, 0L, SEEK_END);
- int file_size = ftell(file);
- // rewind(file);
- // if (max_size < size) size = max_size;
-
- void* mem = map_file(file, file_size, Filemap_Mode_Read);
-
- if (NULL != mem) {
- size = state_read(sys, mem, file_size);
- unmap_file(mem, file_size);
- }
-
- fclose(file);
- }
-
- return size;
- }
-
- int save_state(const nes* sys, const char* cart_filename) {
- int size = -1;
-
- mkdir("save");
-
- char state_filename[FILENAME_MAX] = {0};
- make_state_filename(state_filename, FILENAME_MAX - 1,
- cart_filename);
-
- FILE* file = fopen(state_filename, "w+b");
-
- if (NULL != file) {
- int file_size = state_size(sys);
-
- fseek(file, file_size - 1, SEEK_SET);
- fwrite("", 1, 1, file);
- fflush(file);
-
- void* mem = map_file(file, file_size, Filemap_Mode_Write);
-
- if (NULL != mem) {
- size = state_write(sys, mem, file_size);
- unmap_file(mem, file_size);
- }
-
- fclose(file);
- }
-
- return size;
- }
-
-
- #define tag_def(X) ( \
- X[0] | (X[1] << 8) | (X[2] << 16) | (X[3] << 24) \
- )
-
- #define tag_NESE tag_def("NESE")
- #define tag_cpu tag_def("tCPU")
- #define tag_ppu tag_def("tPPU")
- #define tag_apu tag_def("tAPU")
- #define tag_ram tag_def("WRAM")
- #define tag_mapper tag_def("MAPP")
-
-
- #define szChunkTag sizeof(uint32_t)
- #define szChunkHeader (szChunkTag + sizeof(uint32_t))
-
-
- static int cpu_state_size(const e6502_Core* core) {
- return (sizeof(core->registers) + sizeof(core->pins));
- }
-
- static int cpu_state_read(e6502_Core* core, const void* mem) {
- memcpy(&core->registers, mem, sizeof(core->registers));
- memcpy(&core->pins, mem + sizeof(core->registers),
- sizeof(core->pins));
- return (sizeof(core->registers) + sizeof(core->pins));
- }
-
- static int cpu_state_write(const e6502_Core* core, void* mem) {
- memcpy(mem, &core->registers, sizeof(core->registers));
- memcpy(mem + sizeof(core->registers), &core->pins,
- sizeof(core->pins));
- return (sizeof(core->registers) + sizeof(core->pins));
- }
-
-
- static int ppu_state_size(const nes_ppu* ppu) {
- return ((void*)&(ppu->mapper) - (void*)ppu);
- }
-
- static int ppu_state_read(nes_ppu* ppu, const void* mem) {
- int size = ppu_state_size(ppu);
- memcpy(ppu, mem, size);
- return size;
- }
-
- static int ppu_state_write(const nes_ppu* ppu, void* mem) {
- int size = ppu_state_size(ppu);
- memcpy(mem, ppu, size);
- return size;
- }
-
-
- static int apu_state_size(const nes_apu* apu) {
- return ( ((void*)(apu->channels) - (void*)apu) +
- (5 * ( (void*)&(apu->channels->write) -
- (void*)(apu->channels)
- ) )
- );
- }
-
- static int apu_state_read(nes_apu* apu, const void* mem) {
- int size = ((void*)(apu->channels) - (void*)apu);
- memcpy(apu, mem, size);
- const void* ptr = (mem + size);
- size = ( (void*)&(apu->channels->write) -
- (void*)(apu->channels));
- for (int chan = 0; chan < nes_apu_chan_count; ++chan) {
- memcpy(&apu->channels[chan], ptr, size);
- ptr += size;
- }
- return (ptr - mem);
- }
-
- static int apu_state_write(const nes_apu* apu, void* mem) {
- int size = ((void*)(apu->channels) - (void*)apu);
- memcpy(mem, apu, size);
- void* ptr = (mem + size);
- size = ( (void*)&(apu->channels->write) -
- (void*)(apu->channels));
- for (int chan = 0; chan < nes_apu_chan_count; ++chan) {
- memcpy(ptr, &apu->channels[chan], size);
- ptr += size;
- }
- return (ptr - mem);
- }
-
-
- static int ram_state_size(const nes* sys) {
- return sizeof(sys->ram);
- }
-
- static int ram_state_read(nes* sys, const void* mem) {
- memcpy(sys->ram, mem, sizeof(sys->ram));
- return sizeof(sys->ram);
- }
-
- static int ram_state_write(const nes* sys, void* mem) {
- memcpy(mem, sys->ram, sizeof(sys->ram));
- return sizeof(sys->ram);
- }
-
-
- int state_size(const nes* sys) {
- int size = szChunkHeader;
-
- size += szChunkHeader + cpu_state_size(&sys->cpu);
- size += szChunkHeader + ppu_state_size(&sys->ppu);
- size += szChunkHeader + apu_state_size(&sys->apu);
- // Ignoring input
- size += szChunkHeader + ram_state_size(sys);
- // Cart should already be loaded
- size += szChunkHeader +
- sys->cart.mapper->state_size(sys->cart.map_data);
-
- return size;
- }
-
- static inline void* write_header(void* mem,
- uint32_t tag,
- uint32_t size) {
- memcpy(mem, &tag, szChunkTag);
- mem += szChunkTag;
- memcpy(mem, &size, sizeof(size));
- mem += sizeof(size);
- return mem;
- }
-
- static inline const void* read_header(const void* mem,
- uint32_t* tag,
- uint32_t* size) {
- memcpy(tag, mem, szChunkTag);
- mem += szChunkTag;
- memcpy(size, mem, sizeof(size[0]));
- mem += sizeof(size[0]);
- return mem;
- }
-
- int state_write(const nes* sys, void* mem, int size) {
- void* ptr = mem;
-
- ptr = write_header(ptr, tag_NESE, state_size(sys));
-
- ptr = write_header(ptr, tag_cpu, cpu_state_size(&sys->cpu));
- ptr += cpu_state_write(&sys->cpu, ptr);
-
- ptr = write_header(ptr, tag_ppu, ppu_state_size(&sys->ppu));
- ptr += ppu_state_write(&sys->ppu, ptr);
-
- ptr = write_header(ptr, tag_apu, apu_state_size(&sys->apu));
- ptr += apu_state_write(&sys->apu, ptr);
-
- ptr = write_header(ptr, tag_ram, ram_state_size(sys));
- ptr += ram_state_write(sys, ptr);
-
- const nes_mapper* mapper = sys->cart.mapper;
- const void* map_data = sys->cart.map_data;
- int state_size = mapper->state_size(map_data);
- ptr = write_header(ptr, tag_mapper, state_size);
- ptr += mapper->state_write(map_data, ptr, state_size);
-
- return (ptr - mem);
- }
-
- typedef enum {
- Component_CPU = 0b00000001,
- Component_PPU = 0b00000010,
- Component_APU = 0b00000100,
- Component_RAM = 0b00001000,
- Component_Mapper = 0b00010000,
- } nes_Component;
-
- int state_read(nes* sys, const void* mem, int mem_size) {
- int result = 0;
- nes_Component loaded = 0;
-
- const void* ptr = mem;
- const void* end = (mem + mem_size);
-
- uint32_t tag = 0;
- uint32_t size = 0;
-
- ptr = read_header(mem, &tag, &size);
- if (tag_NESE != tag) {
- result = -1;
- fprintf(stderr,
- "Bad save state magic: %.4s\n",
- (char*)&tag);
- }
-
- while (0 == result && ptr < end) {
- ptr = read_header(ptr, &tag, &size);
- if ((ptr + size) > end) {
- result = -1;
- fprintf(stderr,
- "Unusually large chunk: %.4s: +%d\n",
- (char*)&tag,
- (int)((ptr + size) - end));
- break;
- }
-
- int n_read = 0;
-
- if (tag_cpu == tag) {
- n_read = cpu_state_read(&sys->cpu, ptr);
- loaded |= Component_CPU;
-
- } else if (tag_ppu == tag) {
- n_read = ppu_state_read(&sys->ppu, ptr);
- loaded |= Component_PPU;
-
- } else if (tag_apu == tag) {
- n_read = apu_state_read(&sys->apu, ptr);
- loaded |= Component_APU;
-
- } else if (tag_ram == tag) {
- n_read = ram_state_read(sys, ptr);
- loaded |= Component_RAM;
-
- } else if (tag_mapper == tag) {
- n_read = sys->cart.mapper->state_read(
- sys->cart.map_data, ptr, size
- );
- loaded |= Component_Mapper;
-
- } else {
- fprintf(stderr,
- "Strange chunk: %.4s\n",
- (char*)&tag);
- }
-
- if (n_read != size) {
- result = -1;
- fprintf(stderr,
- "Chunk %.4s: read %d, expected %d\n",
- (char*)&tag, n_read, size);
- }
-
- ptr += size;
- }
-
- if (0 == result) {
- if (!(loaded & Component_CPU)) {
- result = -1;
- fprintf(stderr, "Missing %s state\n", "CPU");
- }
-
- if (!(loaded & Component_PPU)) {
- result = -1;
- fprintf(stderr, "Missing %s state\n", "PPU");
- }
-
- if (!(loaded & Component_APU)) {
- result = -1;
- fprintf(stderr, "Missing %s state\n", "APU");
- }
-
- if (!(loaded & Component_RAM)) {
- result = -1;
- fprintf(stderr, "Missing %s state\n", "RAM");
- }
-
- if (!(loaded & Component_Mapper)) {
- result = -1;
- fprintf(stderr, "Missing %s state\n", "Mapper");
- }
- }
-
- if (0 == result) {
- result = (ptr - mem);
- }
-
- return result;
- }
-
- /*
- // Chunking
-
- int write_chunks(void* dst, int dst_len, const void* src,
- const nese_io_chunk* chunks, int n_chunks) {
- void* ptr = dst;
- for ( void* end = dst + dst_len;
- n_chunks > 0 && ptr < end;
- ptr += chunks->size, --n_chunks, ++chunks) {
- memcpy(ptr, src + chunks->offset, chunks->size);
- }
- return (ptr - dst);
- }
-
- int read_chunks(const void* src, int src_len, void* dst,
- const nese_io_chunk* chunks, int n_chunks) {
- const void* ptr = src;
- for ( const void* end = src + src_len;
- n_chunks > 0 && ptr < end;
- ptr += chunks->size, --n_chunks, ++chunks) {
- memcpy(dst + chunks->offset, ptr, chunks->size);
- }
- return (ptr - dst);
- }
- */
|