#include #include #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); } */