From e87891fccabd451a17a6aec2feb3c073d64b5b7b Mon Sep 17 00:00:00 2001 From: ekeeke Date: Thu, 9 Sep 2021 00:54:45 +0200 Subject: [PATCH] [Core/CD] improved CD audio savestate and CD file seeking (in preparation for MegaSD support) --- core/cd_hw/cdd.c | 432 +++++++++++++++++++++-------------------------- core/cd_hw/cdd.h | 5 +- core/cd_hw/scd.c | 6 +- core/cd_hw/scd.h | 4 +- core/state.c | 6 +- core/state.h | 4 +- 6 files changed, 207 insertions(+), 250 deletions(-) diff --git a/core/cd_hw/cdd.c b/core/cd_hw/cdd.c index 2bf32d3..43be53c 100644 --- a/core/cd_hw/cdd.c +++ b/core/cd_hw/cdd.c @@ -216,6 +216,7 @@ void cdd_reset(void) int cdd_context_save(uint8 *state) { int bufferptr = 0; + unsigned int offset = 0; save_param(&cdd.cycles, sizeof(cdd.cycles)); save_param(&cdd.latency, sizeof(cdd.latency)); @@ -225,37 +226,121 @@ int cdd_context_save(uint8 *state) save_param(&cdd.fader, sizeof(cdd.fader)); save_param(&cdd.status, sizeof(cdd.status)); + /* current track is an audio track ? */ + if (cdd.toc.tracks[cdd.index].type == TYPE_AUDIO) + { + /* get file read offset */ +#if defined(USE_LIBCHDR) + if (cdd.chd.file) + { + /* CHD file offset */ + offset = cdd.chd.hunkofs; + } + else +#endif +#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) + if (cdd.toc.tracks[cdd.index].vf.seekable) + { + /* VORBIS file sample offset */ + offset = ov_pcm_tell(&cdd.toc.tracks[cdd.index].vf); + } + else +#endif + if (cdd.toc.tracks[cdd.index].fd) + { + /* PCM file offset */ + offset = cdStreamTell(cdd.toc.tracks[cdd.index].fd); + } + } + + save_param(&offset, sizeof(offset)); + save_param(&cdd.audio, sizeof(cdd.audio)); + return bufferptr; } -int cdd_context_load(uint8 *state) +int cdd_context_load(uint8 *state, char *version) { - int lba; + unsigned int offset, lba, index; int bufferptr = 0; -#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) -#ifdef DISABLE_MANY_OGG_OPEN_FILES - /* close previous track VORBIS file structure to save memory */ - if (cdd.toc.tracks[cdd.index].vf.datasource) - { - ogg_free(cdd.index); - } -#endif -#endif - load_param(&cdd.cycles, sizeof(cdd.cycles)); load_param(&cdd.latency, sizeof(cdd.latency)); - load_param(&cdd.index, sizeof(cdd.index)); - load_param(&cdd.lba, sizeof(cdd.lba)); + load_param(&index, sizeof(cdd.index)); + load_param(&lba, sizeof(cdd.lba)); load_param(&cdd.scanOffset, sizeof(cdd.scanOffset)); load_param(&cdd.fader, sizeof(cdd.fader)); load_param(&cdd.status, sizeof(cdd.status)); - /* adjust current LBA within track limit */ - lba = cdd.lba; - if (lba < cdd.toc.tracks[cdd.index].start) + /* update current sector */ + cdd.lba = lba; + + /* support for previous state version (1.7.5) */ + if ((version[11] == 0x31) && (version[13] == 0x37) && (version[15] == 0x35)) { - lba = cdd.toc.tracks[cdd.index].start; + /* current track is an audio track ? */ + if (cdd.toc.tracks[index].type == TYPE_AUDIO) + { + /* stay within track limits when seeking files */ + if (lba < cdd.toc.tracks[index].start) + { + lba = cdd.toc.tracks[index].start; + } + + /* seek to current track sector */ + cdd_seek_audio(index, lba); + } + } + else + { + load_param(&offset, sizeof(offset)); + load_param(&cdd.audio, sizeof(cdd.audio)); + + /* current track is an audio track ? */ + if (cdd.toc.tracks[index].type == TYPE_AUDIO) + { +#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) +#ifdef DISABLE_MANY_OGG_OPEN_FILES + /* check if track index has changed */ + if (index != cdd.index) + { + /* close previous track VORBIS file structure to save memory */ + if (cdd.toc.tracks[cdd.index].vf.datasource) + { + ogg_free(cdd.index); + } + + /* open current track VORBIS file */ + if (cdd.toc.tracks[index].vf.seekable) + { + ov_open_callbacks(cdd.toc.tracks[index].fd,&cdd.toc.tracks[index].vf,0,0,cb); + } + } +#endif +#endif + /* seek to current file read offset */ +#if defined(USE_LIBCHDR) + if (cdd.chd.file) + { + /* CHD file offset */ + cdd.chd.hunkofs = offset; + } + else +#endif +#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) + if (cdd.toc.tracks[index].vf.seekable) + { + /* VORBIS file sample offset */ + ov_pcm_seek(&cdd.toc.tracks[index].vf, offset); + } + else +#endif + if (cdd.toc.tracks[index].fd) + { + /* PCM file offset */ + cdStreamSeek(cdd.toc.tracks[index].fd, offset, SEEK_SET); + } + } } /* seek to current subcode position */ @@ -265,36 +350,8 @@ int cdd_context_load(uint8 *state) cdStreamSeek(cdd.toc.sub, lba * 96, SEEK_SET); } - /* seek to current track position */ -#if defined(USE_LIBCHDR) - if (cdd.chd.file) - { - /* CHD file offset */ - cdd.chd.hunkofs = cdd.toc.tracks[cdd.index].offset + (lba * CD_FRAME_SIZE); - } - else -#endif - if (cdd.toc.tracks[cdd.index].type) - { - /* DATA track */ - cdStreamSeek(cdd.toc.tracks[cdd.index].fd, lba * cdd.sectorSize, SEEK_SET); - } -#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) - else if (cdd.toc.tracks[cdd.index].vf.seekable) - { -#ifdef DISABLE_MANY_OGG_OPEN_FILES - /* VORBIS file need to be opened first */ - ov_open_callbacks(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0,cb); -#endif - /* VORBIS AUDIO track */ - ov_pcm_seek(&cdd.toc.tracks[cdd.index].vf, (lba * 588) - cdd.toc.tracks[cdd.index].offset); - } -#endif - else if (cdd.toc.tracks[cdd.index].fd) - { - /* PCM AUDIO track */ - cdStreamSeek(cdd.toc.tracks[cdd.index].fd, (lba * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET); - } + /* update current track index */ + cdd.index = index; return bufferptr; } @@ -1305,6 +1362,52 @@ void cdd_read_data(uint8 *dst, uint8 *subheader) } } +void cdd_seek_audio(int index, int lba) +{ +#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) +#ifdef DISABLE_MANY_OGG_OPEN_FILES + /* check if track index has changed */ + if (index != cdd.index) + { + /* close previous track VORBIS file structure to save memory */ + if (cdd.toc.tracks[cdd.index].vf.datasource) + { + ogg_free(cdd.index); + } + + /* open current track VORBIS file */ + if (cdd.toc.tracks[index].vf.seekable) + { + ov_open_callbacks(cdd.toc.tracks[index].fd,&cdd.toc.tracks[index].vf,0,0,cb); + } + } +#endif +#endif + + /* seek to track position */ +#if defined(USE_LIBCHDR) + if (cdd.chd.file) + { + /* CHD file offset */ + cdd.chd.hunkofs = cdd.toc.tracks[index].offset + (lba * CD_FRAME_SIZE); + } + else +#endif +#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) + if (cdd.toc.tracks[index].vf.seekable) + { + /* VORBIS AUDIO track */ + ov_pcm_seek(&cdd.toc.tracks[index].vf, (lba * 588) - cdd.toc.tracks[index].offset); + } + else +#endif + if (cdd.toc.tracks[index].fd) + { + /* PCM AUDIO track */ + cdStreamSeek(cdd.toc.tracks[index].fd, (lba * 2352) - cdd.toc.tracks[index].offset, SEEK_SET); + } +} + void cdd_read_audio(unsigned int samples) { /* previous audio outputs */ @@ -1637,107 +1740,60 @@ void cdd_update(void) /* check end of current track */ if (cdd.lba >= cdd.toc.tracks[cdd.index].end) { -#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) -#ifdef DISABLE_MANY_OGG_OPEN_FILES - /* close previous track VORBIS file structure to save memory */ - if (cdd.toc.tracks[cdd.index].vf.datasource) - { - ogg_free(cdd.index); - } -#endif -#endif - /* play next track */ + /* seek to next track start (assuming it can only be an audio track) */ + cdd_seek_audio(cdd.index + 1, cdd.toc.tracks[cdd.index + 1].start); + + /* increment current track index */ cdd.index++; /* PAUSE between tracks */ scd.regs[0x36>>1].byte.h = 0x01; - - /* seek to next audio track start */ -#if defined(USE_LIBCHDR) - if (cdd.chd.file) - { - /* CHD file offset */ - cdd.chd.hunkofs = cdd.toc.tracks[cdd.index].offset + (cdd.toc.tracks[cdd.index].start * CD_FRAME_SIZE); - } - else -#endif -#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) - if (cdd.toc.tracks[cdd.index].vf.seekable) - { -#ifdef DISABLE_MANY_OGG_OPEN_FILES - /* VORBIS file need to be opened first */ - ov_open_callbacks(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0,cb); -#endif - ov_pcm_seek(&cdd.toc.tracks[cdd.index].vf, (cdd.toc.tracks[cdd.index].start * 588) - cdd.toc.tracks[cdd.index].offset); - } - else -#endif - if (cdd.toc.tracks[cdd.index].fd) - { - cdStreamSeek(cdd.toc.tracks[cdd.index].fd, (cdd.toc.tracks[cdd.index].start * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET); - } } } /* scanning disc */ else if (cdd.status == CD_SCAN) { + /* current track index */ + int index = cdd.index; + /* fast-forward or fast-rewind */ cdd.lba += cdd.scanOffset; /* check current track limits */ - if (cdd.lba >= cdd.toc.tracks[cdd.index].end) + if (cdd.lba >= cdd.toc.tracks[index].end) { -#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) -#ifdef DISABLE_MANY_OGG_OPEN_FILES - /* close previous track VORBIS file structure to save memory */ - if (cdd.toc.tracks[cdd.index].vf.datasource) - { - ogg_free(cdd.index); - } -#endif -#endif - /* next track */ - cdd.index++; + index++; /* check disc limits */ - if (cdd.index < cdd.toc.last) + if (index < cdd.toc.last) { /* skip directly to next track start position */ - cdd.lba = cdd.toc.tracks[cdd.index].start; + cdd.lba = cdd.toc.tracks[index].start; } else { /* end of disc */ cdd.lba = cdd.toc.end; + cdd.index = cdd.toc.last; cdd.status = CD_END; - /* no AUDIO track playing */ + /* no audio track playing */ scd.regs[0x36>>1].byte.h = 0x01; return; } } - else if (cdd.lba < cdd.toc.tracks[cdd.index].start) + else if (cdd.lba < cdd.toc.tracks[index].start) { -#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) -#ifdef DISABLE_MANY_OGG_OPEN_FILES - /* close previous track VORBIS file structure to save memory */ - if (cdd.toc.tracks[cdd.index].vf.datasource) - { - ogg_free(cdd.index); - } -#endif -#endif - /* check disc limits */ - if (cdd.index > 0) + if (index > 0) { /* previous track */ - cdd.index--; + index--; /* skip directly to previous track end position */ - cdd.lba = cdd.toc.tracks[cdd.index].end; + cdd.lba = cdd.toc.tracks[index].end; } else { @@ -1746,49 +1802,29 @@ void cdd_update(void) } } - /* AUDIO track playing ? */ - scd.regs[0x36>>1].byte.h = cdd.toc.tracks[cdd.index].type ? 0x01 : 0x00; - /* seek to current subcode position */ if (cdd.toc.sub) { cdStreamSeek(cdd.toc.sub, cdd.lba * 96, SEEK_SET); } - /* seek to current track position */ -#if defined(USE_LIBCHDR) - if (cdd.chd.file) + /* current track is an audio track ? */ + if (cdd.toc.tracks[index].type == TYPE_AUDIO) { - /* CHD file offset */ - cdd.chd.hunkofs = cdd.toc.tracks[cdd.index].offset + (cdd.lba * CD_FRAME_SIZE); + /* seek to current track sector */ + cdd_seek_audio(index, cdd.lba); + + /* audio track playing */ + scd.regs[0x36>>1].byte.h = 0x00; } else -#endif - if (cdd.toc.tracks[cdd.index].type) { - /* DATA track */ - cdStreamSeek(cdd.toc.tracks[0].fd, cdd.lba * cdd.sectorSize, SEEK_SET); - } -#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) - else if (cdd.toc.tracks[cdd.index].vf.seekable) - { -#ifdef DISABLE_MANY_OGG_OPEN_FILES - /* check if a new track is being played */ - if (!cdd.toc.tracks[cdd.index].vf.datasource) - { - /* VORBIS file need to be opened first */ - ov_open_callbacks(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0,cb); - } -#endif - /* VORBIS AUDIO track */ - ov_pcm_seek(&cdd.toc.tracks[cdd.index].vf, (cdd.lba * 588) - cdd.toc.tracks[cdd.index].offset); - } -#endif - else if (cdd.toc.tracks[cdd.index].fd) - { - /* PCM AUDIO track */ - cdStreamSeek(cdd.toc.tracks[cdd.index].fd, (cdd.lba * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET); + /* no audio track playing */ + scd.regs[0x36>>1].byte.h = 0x01; } + + /* udpate current track index */ + cdd.index = index; } } @@ -1996,62 +2032,22 @@ void cdd_process(void) /* get track index */ while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++; -#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) -#ifdef DISABLE_MANY_OGG_OPEN_FILES - /* check if track index has changed */ - if (index != cdd.index) + /* audio track ? */ + if (cdd.toc.tracks[index].type == TYPE_AUDIO) { - /* close previous track VORBIS file structure to save memory */ - if (cdd.toc.tracks[cdd.index].vf.datasource) + /* stay within track limits when seeking files */ + if (lba < cdd.toc.tracks[index].start) { - ogg_free(cdd.index); + lba = cdd.toc.tracks[index].start; } - /* open current track VORBIS file */ - if (cdd.toc.tracks[index].vf.seekable) - { - ov_open_callbacks(cdd.toc.tracks[index].fd,&cdd.toc.tracks[index].vf,0,0,cb); - } + /* seek to current track sector */ + cdd_seek_audio(index, lba); } -#endif -#endif /* update current track index */ cdd.index = index; - /* stay within track limits when seeking files */ - if (lba < cdd.toc.tracks[index].start) - { - lba = cdd.toc.tracks[index].start; - } - - /* seek to current track position */ -#if defined(USE_LIBCHDR) - if (cdd.chd.file) - { - /* CHD file offset */ - cdd.chd.hunkofs = cdd.toc.tracks[index].offset + (lba * CD_FRAME_SIZE); - } - else -#endif - if (cdd.toc.tracks[index].type) - { - /* DATA track */ - cdStreamSeek(cdd.toc.tracks[index].fd, lba * cdd.sectorSize, SEEK_SET); - } -#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) - else if (cdd.toc.tracks[index].vf.seekable) - { - /* VORBIS AUDIO track */ - ov_pcm_seek(&cdd.toc.tracks[index].vf, (lba * 588) - cdd.toc.tracks[index].offset); - } -#endif - else if (cdd.toc.tracks[index].fd) - { - /* PCM AUDIO track */ - cdStreamSeek(cdd.toc.tracks[index].fd, (lba * 2352) - cdd.toc.tracks[index].offset, SEEK_SET); - } - /* seek to current subcode position */ if (cdd.toc.sub) { @@ -2103,62 +2099,22 @@ void cdd_process(void) /* get current track index */ while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++; -#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) -#ifdef DISABLE_MANY_OGG_OPEN_FILES - /* check if track index has changed */ - if (index != cdd.index) + /* audio track ? */ + if (cdd.toc.tracks[index].type == TYPE_AUDIO) { - /* close previous track VORBIS file structure to save memory */ - if (cdd.toc.tracks[cdd.index].vf.datasource) + /* stay within track limits when seeking files */ + if (lba < cdd.toc.tracks[index].start) { - ogg_free(cdd.index); + lba = cdd.toc.tracks[index].start; } - /* open current track VORBIS file */ - if (cdd.toc.tracks[index].vf.seekable) - { - ov_open_callbacks(cdd.toc.tracks[index].fd,&cdd.toc.tracks[index].vf,0,0,cb); - } + /* seek to current track sector */ + cdd_seek_audio(index, lba); } -#endif -#endif /* update current track index */ cdd.index = index; - /* stay within track limits */ - if (lba < cdd.toc.tracks[index].start) - { - lba = cdd.toc.tracks[index].start; - } - - /* seek to current track position */ -#if defined(USE_LIBCHDR) - if (cdd.chd.file) - { - /* CHD file offset */ - cdd.chd.hunkofs = cdd.toc.tracks[index].offset + (lba * CD_FRAME_SIZE); - } - else -#endif - if (cdd.toc.tracks[index].type) - { - /* DATA track */ - cdStreamSeek(cdd.toc.tracks[index].fd, lba * cdd.sectorSize, SEEK_SET); - } -#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS) - else if (cdd.toc.tracks[index].vf.seekable) - { - /* VORBIS AUDIO track */ - ov_pcm_seek(&cdd.toc.tracks[index].vf, (lba * 588) - cdd.toc.tracks[index].offset); - } -#endif - else if (cdd.toc.tracks[index].fd) - { - /* PCM AUDIO track */ - cdStreamSeek(cdd.toc.tracks[index].fd, (lba * 2352) - cdd.toc.tracks[index].offset, SEEK_SET); - } - /* seek to current subcode position */ if (cdd.toc.sub) { diff --git a/core/cd_hw/cdd.h b/core/cd_hw/cdd.h index 7428bfc..dae50c2 100644 --- a/core/cd_hw/cdd.h +++ b/core/cd_hw/cdd.h @@ -2,7 +2,7 @@ * Genesis Plus * CD drive processor & CD-DA fader * - * Copyright (C) 2012-2020 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2012-2021 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: @@ -133,10 +133,11 @@ typedef struct extern void cdd_init(int samplerate); extern void cdd_reset(void); extern int cdd_context_save(uint8 *state); -extern int cdd_context_load(uint8 *state); +extern int cdd_context_load(uint8 *state, char *version); extern int cdd_load(char *filename, char *header); extern void cdd_unload(void); extern void cdd_read_data(uint8 *dst, uint8 *subheader); +extern void cdd_seek_audio(int index, int lba); extern void cdd_read_audio(unsigned int samples); extern void cdd_update(void); extern void cdd_process(void); diff --git a/core/cd_hw/scd.c b/core/cd_hw/scd.c index a52b285..d776e45 100644 --- a/core/cd_hw/scd.c +++ b/core/cd_hw/scd.c @@ -2,7 +2,7 @@ * Genesis Plus * Mega CD / Sega CD hardware * - * Copyright (C) 2012-2020 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2012-2021 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: @@ -1851,7 +1851,7 @@ int scd_context_save(uint8 *state) return bufferptr; } -int scd_context_load(uint8 *state) +int scd_context_load(uint8 *state, char *version) { int i; uint16 tmp16; @@ -1873,7 +1873,7 @@ int scd_context_load(uint8 *state) bufferptr += cdc_context_load(&state[bufferptr]); /* CD Drive processor */ - bufferptr += cdd_context_load(&state[bufferptr]); + bufferptr += cdd_context_load(&state[bufferptr], version); /* PCM chip */ bufferptr += pcm_context_load(&state[bufferptr]); diff --git a/core/cd_hw/scd.h b/core/cd_hw/scd.h index ff4d4a6..49b66b0 100644 --- a/core/cd_hw/scd.h +++ b/core/cd_hw/scd.h @@ -2,7 +2,7 @@ * Genesis Plus * Mega CD / Sega CD hardware * - * Copyright (C) 2012-2020 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2012-2021 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: @@ -87,7 +87,7 @@ extern void scd_init(void); extern void scd_reset(int hard); extern void scd_update(unsigned int cycles); extern void scd_end_frame(unsigned int cycles); -extern int scd_context_load(uint8 *state); +extern int scd_context_load(uint8 *state, char *version); extern int scd_context_save(uint8 *state); extern int scd_68k_irq_ack(int level); extern void prg_ram_dma_w(unsigned int words); diff --git a/core/state.c b/core/state.c index dcee54d..79264f1 100644 --- a/core/state.c +++ b/core/state.c @@ -2,7 +2,7 @@ * Genesis Plus * Savestate support * - * Copyright (C) 2007-2019 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2007-2021 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: @@ -149,7 +149,7 @@ int state_load(unsigned char *state) load_param(&tmp32, 4); m68k_set_reg(M68K_REG_USP,tmp32); load_param(&tmp32, 4); m68k_set_reg(M68K_REG_ISP,tmp32); - load_param(&m68k.cycles, sizeof(m68k.cycles)); + load_param(&m68k.cycles, sizeof(m68k.cycles)); load_param(&m68k.int_level, sizeof(m68k.int_level)); load_param(&m68k.stopped, sizeof(m68k.stopped)); } @@ -173,7 +173,7 @@ int state_load(unsigned char *state) } /* CD hardware */ - bufferptr += scd_context_load(&state[bufferptr]); + bufferptr += scd_context_load(&state[bufferptr], version); } else if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { diff --git a/core/state.h b/core/state.h index 03ffa45..7f6e46b 100644 --- a/core/state.h +++ b/core/state.h @@ -2,7 +2,7 @@ * Genesis Plus * Savestate support * - * Copyright (C) 2007-2019 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2007-2021 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: @@ -40,7 +40,7 @@ #define _STATE_H_ #define STATE_SIZE 0xfd000 -#define STATE_VERSION "GENPLUS-GX 1.7.5" +#define STATE_VERSION "GENPLUS-GX 1.7.6" #define load_param(param, size) \ memcpy(param, &state[bufferptr], size); \