Browse Source

Add save/load state support

- Works with CHR RAM, still needs thorough testing
v2
Nathaniel Walizer 8 months ago
parent
commit
c3a0dfbf42
22 changed files with 510 additions and 35 deletions
  1. +1
    -0
      Makefile
  2. +9
    -1
      src/f6502.c
  3. +12
    -1
      src/f6502.h
  4. +2
    -0
      src/input.h
  5. +56
    -13
      src/linux/port.c
  6. +4
    -1
      src/map/map001.c
  7. +2
    -1
      src/map/map002.c
  8. +2
    -1
      src/map/map003.c
  9. +10
    -6
      src/map/map004.c
  10. +24
    -0
      src/mapper.c
  11. +5
    -0
      src/mapper.h
  12. +10
    -0
      src/memory.c
  13. +16
    -6
      src/memory.h
  14. +14
    -1
      src/nes.c
  15. +8
    -0
      src/nes.h
  16. +12
    -0
      src/port.h
  17. +39
    -0
      src/ppu.c
  18. +13
    -4
      src/ppu.h
  19. +178
    -0
      src/save.c
  20. +11
    -0
      src/save.h
  21. +53
    -0
      src/serdes.c
  22. +29
    -0
      src/serdes.h

+ 1
- 0
Makefile View File

@@ -30,6 +30,7 @@ MAPDIR = $(SRCDIR)/map
NESE_SRC_SRCS = f6502.c f6502_opcodes.c
NESE_SRC_SRCS += nese.c nes.c cart.c mapper.c
NESE_SRC_SRCS += ppu.c apu.c
NESE_SRC_SRCS += memory.c serdes.c save.c
NESE_SRC_SRCS += $(OS)/port.c
NESE_MAP_SRCS = $(notdir $(wildcard $(MAPDIR)/*.c))



+ 9
- 1
src/f6502.c View File

@@ -186,7 +186,6 @@ static inline int f6502_write(nes_Memory* mem,
mem->ppu.t &= ~(ppu_Control_Nametable_Mask << 10);
mem->ppu.t |= ((uint16_t)val & ppu_Control_Nametable_Mask) << 10;
mem->ppu.addr_inc = (val & ppu_Control_VRAM_Inc) ? 32 : 1;
// TODO: Trigger NMI if VBlank status is set?
break;

case ppu_reg_mask:
@@ -1725,3 +1724,12 @@ int f6502_step(f6502_Core* core, int clocks) {
core->clocks += clocks_elapsed;
return clocks_elapsed;
}


/* Ser/Des */

const Serdes_Item f6502_serdes[] = {
{offsetof(f6502_Core, registers), serdes_copy, serdes_copy, serdes_copy_size, (void*)(sizeof(f6502_Registers) + sizeof(f6502_Interrupt))},
{offsetof(f6502_Core, memory), deserialize, serialize, serdes_size, nes_memory_serdes},
{0}
};

+ 12
- 1
src/f6502.h View File

@@ -1,6 +1,9 @@
#ifndef F6502_H_
#define F6502_H_

#include "serdes.h"


#include <stdbool.h>
#include <stdint.h>

@@ -41,10 +44,15 @@ typedef enum {
} f6502_Interrupt;

struct f6502_Core {
uint64_t clocks;
// Static
f6502_Registers registers;
f6502_Interrupt interrupts;

// Specific
nes_Memory memory;

// Don't care
uint64_t clocks;
};
typedef struct f6502_Core f6502_Core;

@@ -55,4 +63,7 @@ void f6502_set_NMI(f6502_Core*, bool active);
void f6502_set_IRQ(f6502_Core*, bool active);


extern const Serdes_Item f6502_serdes[];


#endif // F6502_H_

+ 2
- 0
src/input.h View File

@@ -1,6 +1,8 @@
#ifndef NESE_INPUT_H_
#define NESE_INPUT_H_

#include <stdint.h>


#define nes_controller_bus_mask (0b11111000)



+ 56
- 13
src/linux/port.c View File

@@ -2,14 +2,18 @@
#include <stdio.h>

#include <time.h>
#include <sys/mman.h>
#include <unistd.h>

#include <sys/mman.h>
#include <sys/stat.h>


#include <SDL.h>

#include "cart.h"
#include "nese.h"
#include "port.h"
#include "save.h"

#define DEBUG "Port"
#include "log.h"
@@ -20,20 +24,28 @@
* Memory mapping specifically needs to be ported for each OS
*/

static void* nese_map_file(FILE* file, int size) {
void* addr = mmap(NULL, size, PROT_READ, MAP_SHARED,
fileno(file), 0);
if (MAP_FAILED == addr || NULL == addr) {
fprintf(stderr, "mmap failed: %d\n", (int)errno);
addr = NULL;
void* nese_map_file(FILE* file, int size, Filemap_Mode map_mode) {
int prot = ( Filemap_Mode_Write == map_mode ?
PROT_WRITE : PROT_READ);
int flags = ( Filemap_Mode_Write == map_mode ?
MAP_SHARED : MAP_PRIVATE);
void* mem = mmap(NULL, size, prot, flags, fileno(file), 0);
if ((void*)-1 == mem) {
fprintf(stderr, "Failed to map file: %d\n", errno);
mem = NULL;
}
return addr;
return mem;
}

static int nese_unmap_file(void* addr, int size) {
return munmap(addr, size);
void nese_unmap_file(void* mem, int size) {
munmap(mem, size);
}

int nese_mkdir(const char* dir) {
return mkdir(dir, 0777);
}


static int nese_file_size(FILE* file) {
int size = -1;
if (0 == fseek(file, 0, SEEK_END)) {
@@ -89,13 +101,26 @@ typedef struct {

/* Input */

typedef enum {
Action_Error = -1,
Action_OK,
Action_Quit,
Action_Reset,
Action_Load,
Action_Save,
Action_Menu,
Action_Cancel,
} nese_Action;

int nese_update_input(void* plat_data, nes_Input* input) {
// Gamepad states are already updated in nese_frame_ready()
return 0;
}


static const int sdl_alt_start_key = SDLK_RETURN;
#define sdl_save_key (SDLK_F1)
#define sdl_load_key (SDLK_F2)
#define sdl_alt_start_key (SDLK_RETURN)

static const int sdl_keycodes[nes_controller_num_buttons] = {
SDLK_a,
@@ -140,6 +165,17 @@ static int process_events(nes* sys) {
} else {
input->gamepads[0].buttons &= ~mask;
}

} else if (SDL_KEYDOWN == event.type) {
switch (event.key.keysym.sym) {
case sdl_save_key:
status = Action_Save;
break;

case sdl_load_key:
status = Action_Load;
break;
}
}
// TODO: Menu or other hotkeys
}
@@ -277,7 +313,13 @@ int nese_frame_ready(void* plat_data) {

status = process_events(plat->sys);

// TODO: Perform menu actions
if (Action_Save == status) {
status = save_state(plat->sys, plat->cart.filename);
} else if (Action_Load == status) {
status = load_state(plat->sys, plat->cart.filename);
}

// TODO: Perform more actions

if (0 == status) {
plat->t_target += FRAME_TIME_NS;
@@ -397,7 +439,8 @@ static int load_cart(const char* filename, platform_data* plat) {
status = -1;
} else {
filesize = nese_file_size(file);
cart_data = nese_map_file(file, filesize);
cart_data = nese_map_file(file, filesize,
Filemap_Mode_Read);
if (NULL == cart_data) {
fprintf(stderr, "Failed to map %s\n", filename);
status = -1;


+ 4
- 1
src/map/map001.c View File

@@ -88,7 +88,10 @@ static void map001_reset(nes_Mapper* map, nes_Memory* mem) {

static int map001_init(nes_Mapper* map, const ines_Header* hdr,
nes_Memory* mem) {
map001_reset(map, mem);
map001_data* data = (map001_data*)map->data;
mmc1_update_prg(data, mem);
mmc1_update_chr(data, mem);
mmc1_update_vram(data, mem);
return 0;
}



+ 2
- 1
src/map/map002.c View File

@@ -23,7 +23,8 @@ static int map002_init(nes_Mapper* map, const ines_Header* hdr,
int last_bank = (mem->n_rom_banks / 2) - 1;
mem->rom_bank[2] = prg_rom_page(mem, (last_bank * 2) + 0);
mem->rom_bank[3] = prg_rom_page(mem, (last_bank * 2) + 1);
map002_reset(map, mem);
uxrom_set_bank((map002_data*)map->data, mem,
((map002_data*)map->data)->bank);
return 0;
}



+ 2
- 1
src/map/map003.c View File

@@ -26,7 +26,8 @@ static void map003_reset(nes_Mapper* map, nes_Memory* mem) {

static int map003_init(nes_Mapper* map, const ines_Header* hdr,
nes_Memory* mem) {
map003_reset(map, mem);
cnrom_set_bank((map003_data*)map->data, mem,
((map003_data*)map->data)->bank);
return 0;
}



+ 10
- 6
src/map/map004.c View File

@@ -129,18 +129,22 @@ static void map004_reset(nes_Mapper* map, nes_Memory* mem) {
data->irq_count = 0;
data->irq_latch = 0;
mmc3_update_rom_mode(data, mem, 0);
mmc3_map_prg(mem, 3, mem->n_rom_banks - 1);
mmc3_update_vram(data, mem);
}

static int map004_init(nes_Mapper* map, const ines_Header* hdr,
nes_Memory* mem) {
map004_data* data = (map004_data*)map->data;
data->flags = (hdr->flags_6 & ines_Flag_Vert_Mirror) ?
mmc3_Flag_Horizontal : 0;
data->bank_select = mmc3_Bank_Select_PRG |
mmc3_Bank_Select_CHR;
map004_reset(map, mem);
if (hdr) {
data->flags = (hdr->flags_6 & ines_Flag_Vert_Mirror) ?
mmc3_Flag_Horizontal : 0;
}
uint8_t bank_select = data->bank_select;
data->bank_select ^= mmc3_Bank_Select_PRG |
mmc3_Bank_Select_CHR;
mmc3_update_rom_mode(data, mem, bank_select);
mmc3_map_prg(mem, 3, mem->n_rom_banks - 1);
mmc3_update_vram(data, mem);
return 0;
}



+ 24
- 0
src/mapper.c View File

@@ -1,3 +1,5 @@
#include <string.h>

#include "mapper.h"


@@ -14,3 +16,25 @@ const nes_Mapper* nes_mappers[256] = {
&map003,
&map004,
};


/* Ser/Des */

int mapper_serialize(void* dst, const void* src, int avail, const void*) {
const nes_Mapper* mapper = (const nes_Mapper*)src;
if (avail > mapper->data_size) avail = mapper->data_size;
memcpy(dst, mapper->data, avail);
return avail;
}

int mapper_deserialize(void* dst, const void* src, int avail, const void*) {
nes_Mapper* mapper = (nes_Mapper*)dst;
if (avail > mapper->data_size) avail = mapper->data_size;
memcpy(mapper->data, src, avail);
return avail;
}

int mapper_serdes_size(const void* _mapper, const void*) {
const nes_Mapper* mapper = (const nes_Mapper*)_mapper;
return mapper->data_size;
}

+ 5
- 0
src/mapper.h View File

@@ -38,4 +38,9 @@ typedef struct nes_Mapper nes_Mapper;
extern const nes_Mapper* nes_mappers[256];


int mapper_serialize(void* dst, const void* src, int avail, const void*);
int mapper_deserialize(void* dst, const void* src, int avail, const void*);
int mapper_serdes_size(const void* src, const void*);


#endif // NESE_MAPPER_H_

+ 10
- 0
src/memory.c View File

@@ -0,0 +1,10 @@
#include "memory.h"


const Serdes_Item nes_memory_serdes[] = {
{offsetof(nes_Memory, mapper), mapper_deserialize, mapper_serialize, mapper_serdes_size},
{offsetof(nes_Memory, ppu), deserialize, serialize, serdes_size, nes_ppu_memory_serdes},
{offsetof(nes_Memory, input), serdes_copy, serdes_copy, serdes_copy_size, (void*)(sizeof(nes_Memory) - offsetof(nes_Memory, input))},
{0},
};


+ 16
- 6
src/memory.h View File

@@ -8,6 +8,8 @@
#include "mapper.h"
#include "ppu.h"

#include "serdes.h"


#define NES_RAM_SIZE (0x2000U)
#define NES_SRAM_SIZE (0x2000U)
@@ -23,18 +25,23 @@ struct nes_Memory {
#ifdef F6502_FLAT
uint8_t ram[65536];
#else
uint8_t ram[NES_RAM_SIZE / 4]; // Mirrored 3x
uint8_t sram[NES_SRAM_SIZE];
nes_Memory_Flag flags;
uint8_t reserved[3];
// Dynamic (set on init/reload)
uint8_t* sram_bank; // Mapped to 0x6000 - 0x7FFF
uint8_t* rom;
uint8_t* rom_bank[4];
int n_rom_banks;
nes_PPU_Memory ppu;
// Dynamic (specific init/reload)
nes_Mapper mapper;
nes_PPU_Memory ppu;

// Static
nes_Input input;
nes_APU_Memory apu;

uint8_t ram[NES_RAM_SIZE / 4]; // Mirrored 3x
uint8_t sram[NES_SRAM_SIZE];
nes_Memory_Flag flags;
int n_rom_banks;
#endif
};
typedef struct nes_Memory nes_Memory;
@@ -45,4 +52,7 @@ static inline uint8_t* prg_rom_page(nes_Memory* mem, int page) {
}


extern const Serdes_Item nes_memory_serdes[];


#endif // NESE_MEMORY_H_

+ 14
- 1
src/nes.c View File

@@ -3,7 +3,7 @@
#include "nes.h"
#include "port.h"

#define NESE_DEBUG "NES"
//#define NESE_DEBUG "NES"
#include "log.h"


@@ -16,6 +16,10 @@ void nes_init(nes* sys, void* plat) {

void nes_reset(nes* sys) {
f6502_reset(&sys->core);
nes_Mapper* mapper = &sys->core.memory.mapper;
if (mapper->reset) {
mapper->reset(mapper, &sys->core.memory);
}
// TODO: Reset PPU
// TODO: Reset APU
}
@@ -191,3 +195,12 @@ int nes_loop(nes* sys, void* plat) {

return status;
}


/* Ser/Des */

const Serdes_Item nes_serdes[] = {
{offsetof(nes, core), deserialize, serialize, serdes_size, f6502_serdes},
{offsetof(nes, ppu), serdes_copy, serdes_copy, serdes_copy_size, (void*)(sizeof(nes_PPU) + sizeof(nes_APU))},
{0}
};

+ 8
- 0
src/nes.h View File

@@ -8,8 +8,13 @@


typedef struct {
// Already set
const ines_Header* cart_header;

// Specific
f6502_Core core;

// Static
nes_PPU ppu;
nes_APU apu;
} nes;
@@ -20,4 +25,7 @@ void nes_done(nes*);
int nes_loop(nes*, void*);


extern const Serdes_Item nes_serdes[];


#endif // NES_H_

+ 12
- 0
src/port.h View File

@@ -6,12 +6,24 @@
#include "input.h"


typedef enum {
Filemap_Mode_Read = 0,
Filemap_Mode_Write,
} Filemap_Mode;

void* nese_map_file(FILE* file, int size, Filemap_Mode);
void nese_unmap_file(void* mem, int size);

int nese_mkdir(const char* dirname);


int nese_frame_start(void*, uint8_t background);
int nese_line_ready(void*, uint8_t* buffer, int line);
int nese_frame_ready(void*);
int nese_update_input(void*, nes_Input*);
int nese_get_audio_frequency(void*);


void* nese_alloc_gpu(int);
void* nese_alloc_cpu(int);
void* nese_alloc(int);


+ 39
- 0
src/ppu.c View File

@@ -5,6 +5,8 @@
//#define NESE_DEBUG "PPU"
#include "log.h"

#include "serdes.h"


void nes_ppu_init(nes_PPU* ppu, nes_PPU_Memory* mem) {
uint8_t* pal = mem->palette;
@@ -362,3 +364,40 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
((y_scroll & 0xF8U) << 2);
}
}

static int nes_ppu_chr_ram_size(const void* _ppu, const void*) {
const nes_PPU_Memory* ppu = (const nes_PPU_Memory*)_ppu;
return (ppu->chr_ram ?
ppu->n_chr_banks * NES_CHR_ROM_PAGE_SIZE :
0);
}

static int nes_ppu_read_chr_ram(void* dst, const void* src,
int avail, const void*) {
int size = 0;
nes_PPU_Memory* ppu = (nes_PPU_Memory*)dst;
if (ppu->chr_ram) {
size = ppu->n_chr_banks * NES_CHR_ROM_PAGE_SIZE;
if (size > avail) size = avail;
memcpy(ppu->chr_ram, src, size);
}
return size;
}

static int nes_ppu_write_chr_ram(void* dst, const void* src,
int avail, const void*) {
int size = 0;
const nes_PPU_Memory* ppu = (const nes_PPU_Memory*)src;
if (ppu->chr_ram) {
size = ppu->n_chr_banks * NES_CHR_ROM_PAGE_SIZE;
if (size > avail) size = avail;
memcpy(dst, ppu->chr_ram, size);
}
return size;
}

const Serdes_Item nes_ppu_memory_serdes[] = {
{0, nes_ppu_read_chr_ram, nes_ppu_write_chr_ram, nes_ppu_chr_ram_size},
{offsetof(nes_PPU_Memory, ctrl), serdes_copy, serdes_copy, serdes_copy_size, (void*)(sizeof(nes_PPU_Memory) - offsetof(nes_PPU_Memory, ctrl))},
{0}
};

+ 13
- 4
src/ppu.h View File

@@ -3,6 +3,8 @@

#include <stdint.h>

#include "serdes.h"


#define nes_ppu_render_w (256U)
#define nes_ppu_render_h (240U)
@@ -83,6 +85,13 @@ typedef enum {
#define NES_PPU_PAL_START (0x3F00U)

typedef struct {
// Dynamic memory banks
uint8_t* chr;
uint8_t* bank[16];

// Nullable buffer
uint8_t* chr_ram;

// Registers &c.
uint8_t ctrl;
uint8_t mask;
@@ -95,11 +104,8 @@ typedef struct {
uint8_t latch; // aka "w" - TODO: Could this be a flag?
uint8_t addr_inc; // Auto-increment (1 or 32)

// Memory banks
uint8_t* chr;
// Static memory banks
int n_chr_banks;
uint8_t* chr_ram; // TODO: Could this be a flag?
uint8_t* bank[16];
uint8_t palette[32]; // Rendering palette with transparency masked
uint8_t vram[NES_PPU_VRAM_SIZE];
uint8_t pal_bank[NES_CHR_ROM_PAGE_SIZE]; // Raw palette data mirrored in banks 12-15, also pal
@@ -143,4 +149,7 @@ void nes_ppu_find_hit_line(nes_PPU*, nes_PPU_Memory*);
void nes_ppu_render_line(nes_PPU*, nes_PPU_Memory*);


extern const Serdes_Item nes_ppu_memory_serdes[];


#endif // NESE_PPU_H_

+ 178
- 0
src/save.c View File

@@ -0,0 +1,178 @@
#include <stdio.h>
#include <string.h>

#include "port.h"
#include "serdes.h"
#include "save.h"


/* File Helpers */

static const char* basename(const char* filename) {
const char* slash = filename + strlen(filename) - 1;
for ( ; slash >= filename &&
*slash != '\\' &&
*slash != '/';
--slash );
return &slash[1];
}

static int make_filename(char* filename, int max_len,
const char* orig_name,
const char* subdir, const char* ext) {
int status = 0;
int remain = max_len;

const char* orig_base = basename(orig_name);
const char* orig_dot = strrchr(orig_base, '.');

int orig_path_len = orig_base - orig_name;

int orig_base_len = (NULL == orig_dot) ?
strlen(orig_base) :
(orig_dot - orig_base);

// Part 1/4: Leading path
if (0 == status && orig_path_len <= remain) {
memcpy(filename, orig_name, orig_path_len);
remain -= orig_path_len;
filename += orig_path_len;
} else {
status = -1;
}

// Part 2/4: Subdirectory
if (0 == status && NULL != subdir) {
int subdir_len = strlen(subdir);
if ((subdir_len + 1) <= remain) {
memcpy(filename, subdir, subdir_len);
filename += subdir_len;
*filename++ = '/';
remain -= (subdir_len + 1);
} else {
status = -1;
}
}

// Part 3/4: Basename
if (0 == status && orig_base_len <= remain) {
memcpy(filename, orig_name, orig_base_len);
remain -= orig_base_len;
filename += orig_base_len;
} else {
status = -1;
}

// Part 4/4: Extension
if (0 == status && NULL != ext) {
int ext_len = strlen(ext);
if ((ext_len + 1) <= remain) {
*filename++ = '.';
memcpy(filename, ext, ext_len);
remain -= (ext_len + 1);
filename += ext_len;
} else {
status = -1;
}
}

return (status < 0 ? status : (max_len - remain));
}


/* System State */

static int make_state_filename(char* save_filename, int max_len,
const char* cart_filename) {
return make_filename( save_filename, max_len,
basename(cart_filename),
"save", "nese");
}

static int state_size(const nes* sys) {
return serdes_size(sys, nes_serdes);
}

static int state_read(nes* sys, const void* mem, int size) {
return deserialize(sys, mem, size, nes_serdes);
}

static int state_write(const nes* sys, void* mem, int size) {
return serialize(mem, sys, size, nes_serdes);
}

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 = nese_map_file(file, file_size,
Filemap_Mode_Read);

if (NULL != mem) {
size = state_read(sys, mem, file_size);
nese_unmap_file(mem, file_size);
}

fclose(file);

if (file_size == size) {
nes_Mapper* mapper = &sys->core.memory.mapper;
if (mapper->init) {
size =mapper->init(mapper, NULL, &sys->core.memory);
} else {
size = 0;
}
} else {
size = -1;
}
}

return size;
}

int save_state(const nes* sys, const char* cart_filename) {
int size = -1;

nese_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 = nese_map_file(file, file_size,
Filemap_Mode_Write);

if (NULL != mem) {
size = state_write(sys, mem, file_size);
nese_unmap_file(mem, file_size);
}

fclose(file);

size = (size == file_size ? 0 : -1);
}

return size;
}

+ 11
- 0
src/save.h View File

@@ -0,0 +1,11 @@
#ifndef NESE_SAVE_H_
#define NESE_SAVE_H_

#include "nes.h"


int load_state(nes*, const char* filename);
int save_state(const nes*, const char* filename);


#endif // NESE_SAVE_H_

+ 53
- 0
src/serdes.c View File

@@ -0,0 +1,53 @@
#include <string.h>

#include "serdes.h"


int serialize(void* dst, const void* src, int avail,
const void* _list) {
const Serdes_Item* list = (const Serdes_Item*)_list;
void* dst_ptr = dst;
for ( ; list->write; ++list) {
const void* src_ptr = src + list->offset;
int count = list->write(dst_ptr, src_ptr, avail, list->arg);
if (count < 0) break;
dst_ptr += count;
avail -= count;
}
return (dst_ptr - dst);
}

int deserialize(void* dst, const void* src, int avail,
const void* _list) {
const Serdes_Item* list = (const Serdes_Item*)_list;
const void* src_ptr = src;
for ( ; list->read; ++list) {
void* dst_ptr = dst + list->offset;
int count = list->read(dst_ptr, src_ptr, avail, list->arg);
if (count < 0) break;
src_ptr += count;
avail -= count;
}
return (src_ptr - src);
}

int serdes_size(const void* src, const void* _list) {
const Serdes_Item* list = (const Serdes_Item*)_list;
int size = 0;
for ( ; list->size; ++list) {
size += list->size(src + list->offset, list->arg);
}
return size;
}

int serdes_copy(void* dst, const void* src, int avail,
const void* arg) {
int size = (long)arg;
if (size > avail) size = avail;
memcpy(dst, src, size);
return size;
}

int serdes_copy_size(const void*, const void* arg) {
return (long)arg;
}

+ 29
- 0
src/serdes.h View File

@@ -0,0 +1,29 @@
#ifndef NESE_SERDES_H_
#define NESE_SERDES_H_

#include <stddef.h>


typedef int(*serdes_io_fn)(void*, const void*, int, const void*);
typedef int(*serdes_size_fn)(const void*, const void*);

typedef struct {
long offset;
int (*read)(void* dst, const void* src, int avail, const void* arg);
int (*write)(void* dst, const void* src, int avail, const void* arg);
int (*size)(const void*, const void*);
const void* arg;
} Serdes_Item;


int serialize(void* dst, const void* src, int avail, const void* list);
int deserialize(void* dst, const void* src, int avail, const void* list);
int serdes_size(const void*, const void* list);

int serdes_copy(void* dst, const void* src, int avail, const void* arg);
int serdes_copy_size(const void*, const void* arg);

// TODO: Compression (Maybe just RLE)


#endif // NESE_SERDES_H_

Loading…
Cancel
Save