home *** CD-ROM | disk | FTP | other *** search
- #include <stdarg.h>
-
- #include "v9t9_common.h"
- #include "vdp.h"
- #include "sound.h"
- #include "demo.h"
-
- #define _L LOG_DEMO
-
- static OSSpec demo_spec;
- static OSRef demo_ref;
- static OSHandle demo_handle;
- static bool demo_recording;
-
- /*
- * Append data to demo.
- */
- static void handle_append(OSHandle *handle, void *data, OSSize size)
- {
- OSError err;
-
- if (demo_recording) {
- err = OS_AppendHandle(handle, data, size);
-
- if (err != OS_NOERR) {
- logger(_L|LOG_ERROR|LOG_USER, "Could not add %d bytes to demo (%s)\n",
- size, OS_GetErrText(err));
- demo_stop_record();
- }
- }
- }
-
- typedef struct
- {
- int type, idx, max;
- u8 *data;
- } demo_buffer;
-
- static demo_buffer buffer_video, buffer_sound, buffer_speech;
- static u16 vdp_addr_next;
-
- /*
- * Initialize buffer for reading or writing.
- */
- static void buffer_init(demo_buffer *buffer, int size, int type)
- {
- buffer->type = type;
- buffer->idx = 0;
- buffer->max = size;
- if (buffer->data) xfree(buffer->data);
- buffer->data = (u8 *)xmalloc(size);
- }
-
- /*
- * Terminate use of a buffer.
- */
- static void buffer_term(demo_buffer *buffer)
- {
- if (buffer->data)
- xfree(buffer->data);
- memset(buffer, 0, sizeof(demo_buffer));
- }
-
- /*
- * Set up a buffer for reading or writing.
- */
- static void buffer_setup(demo_buffer *buffer)
- {
- buffer->idx = 0;
- }
-
- /*
- * Flush a buffer to the file, with a type and length header.
- */
- static void buffer_flush(demo_buffer *buffer)
- {
- u8 header[4];
-
- // write event type and buffer length
- header[0] = buffer->type;
- header[1] = (buffer->idx & 0xff);
- header[2] = (buffer->idx >> 8) & 0xff;
- handle_append(&demo_handle, (void *)header, 3);
-
- // write data
- handle_append(&demo_handle, (void *)buffer->data, buffer->idx);
-
- buffer->idx = 0;
- }
-
- /*
- * Write data to a buffer
- */
- static void buffer_write(demo_buffer *buffer, void *data, int len)
- {
- if (buffer->idx + len > buffer->max) {
- buffer_flush(buffer);
- }
- memcpy((u8 *)buffer->data + buffer->idx, data, len);
- buffer->idx += len;
- }
-
- /*
- * Write data into a buffer
- */
- static void buffer_poke(demo_buffer *buffer, int offset, void *data, int len)
- {
- if (offset + len > buffer->idx) {
- logger(_L|LOG_FATAL, "buffer_poke: overrun (%d+%d > %d)\n",
- offset, len, buffer->idx);
- }
- memcpy((u8 *)buffer->data + offset, data, len);
- }
-
- void demo_init(void)
- {
- // these constants were hardcoded in v9t9 6.0 and
- // should probably be kept this way
- buffer_init(&buffer_video, 8192, demo_type_video);
- buffer_init(&buffer_sound, 1024, demo_type_sound);
- buffer_init(&buffer_speech, 512, demo_type_speech);
- }
-
- void demo_term(void)
- {
- buffer_term(&buffer_video);
- buffer_term(&buffer_sound);
- buffer_term(&buffer_speech);
- }
-
- int demo_start_record(OSSpec *spec)
- {
- OSError err;
- int addr;
-
- logger(_L|L_1, "Setting up demo (%s)\n", OS_SpecToString1(spec));
-
- /* Create file first */
- demo_spec = *spec;
- err = OS_Create(spec, &OS_TEXTTYPE);
- if (err != OS_NOERR) {
- logger(_L|LOG_ERROR|LOG_USER, "Could not create demo file '%s' (%s)\n",
- OS_SpecToString1(spec), OS_GetErrText(err));
- return 0;
- }
-
- /* Store all data in a memory handle */
- err = OS_NewHandle(0, &demo_handle);
- if (err != OS_NOERR) {
- logger(_L|LOG_ERROR|LOG_USER, "Could not allocate memory for demo (%s)\n",
- OS_GetErrText(err));
- return 0;
- }
-
- /* Clear buffers */
- buffer_setup(&buffer_video);
- buffer_setup(&buffer_sound);
- buffer_setup(&buffer_speech);
-
- /* Write initial data */
- demo_recording = true;
- stateflag |= ST_DEMOING;
-
- /* Magic header */
- handle_append(&demo_handle, DEMO_MAGIC_HEADER, sizeof(DEMO_MAGIC_HEADER));
-
- /* Write VDP regs */
- for (addr = 0; addr < 8; addr++) {
- demo_record_event(demo_type_video,
- 0x8000 + (addr << 8) + vdpregs[addr]);
- }
-
- /* Write VDP memory */
- for (addr = 0; addr < 0x4000; addr++) {
- demo_record_event(demo_type_video,
- 0x4000 + addr,
- vdp_mmio_read(addr));
- }
- buffer_flush(&buffer_video);
-
- /* Write sound data */
- for (addr = 0; addr < 4; addr++) {
- u8 base = 0x80 + (addr << 5);
-
- /* tone/noise bytes */
- demo_record_event(demo_type_sound,
- sound_voices[addr].operation[OPERATION_FREQUENCY_LO]);
- demo_record_event(demo_type_sound,
- sound_voices[addr].operation[OPERATION_FREQUENCY_HI]);
- demo_record_event(demo_type_sound,
- sound_voices[addr].operation[OPERATION_ATTENUATION]);
-
- /* volume */
- demo_record_event(demo_type_sound,
- base + sound_voices[addr].volume);
- }
- buffer_flush(&buffer_sound);
-
- /* Write speech data */
- demo_record_event(demo_type_speech, demo_speech_terminating);
- buffer_flush(&buffer_speech);
-
- return 1;
- }
-
- void demo_pause_record(bool pausing)
- {
- demo_record_event(demo_type_tick);
- if (pausing)
- stateflag &= ~ST_DEMOING;
- else
- stateflag |= ST_DEMOING;
- }
-
- void demo_stop_record(void)
- {
- OSError err;
- OSRef demo_ref;
- OSSize size;
- void *ptr;
-
- logger(_L|L_1, "Closing demo\n");
- if (demo_recording) {
- logger(_L|L_1, "Closing demo (%s)\n", OS_SpecToString1(&demo_spec));
-
- buffer_flush(&buffer_video);
- buffer_flush(&buffer_sound);
- buffer_flush(&buffer_speech);
-
- demo_recording = false;
-
- if ((err = OS_Open(&demo_spec, OSWrite, &demo_ref)) == OS_NOERR &&
- (err = OS_GetHandleSize(&demo_handle, &size)) == OS_NOERR &&
- (ptr = OS_LockHandle(&demo_handle)) != 0L &&
- (err = OS_Write(demo_ref, (void *)ptr, &size)) == OS_NOERR)
- {
- OS_UnlockHandle(&demo_handle);
- OS_FreeHandle(&demo_handle);
- OS_Close(demo_ref);
- }
- else {
- logger(_L|LOG_USER|LOG_ERROR, "Could not write demo file '%s' (%s)\n",
- OS_SpecToString1(&demo_spec), OS_GetErrText(err));
- }
- stateflag &= ~ST_DEMOING;
- }
- }
-
- /*
- * Main interface to demo saving engine
- */
- int demo_record_event(demo_type type, ...)
- {
- va_list va;
- int arg;
- u8 data[8];
-
- va_start(va, type);
- switch (type)
- {
- case demo_type_tick:
- /* flush all current data */
- logger(_L|L_1, "Tick\n");
- data[0] = type;
- handle_append(&demo_handle, (void *)data, 1);
- buffer_flush(&buffer_video);
- buffer_flush(&buffer_sound);
- buffer_flush(&buffer_speech);
- break;
-
- /* for these events, buffer */
-
- case demo_type_video:
- /* if address is different than the last one
- plus one, flush the buffer */
- arg = va_arg(va, int);
-
- my_assert(buffer_video.max == 256);
-
- logger(_L|L_2, "Video >%04X", arg);
- if (arg != vdp_addr_next // contiguous
- || buffer_video.idx == 0 // empty
- || buffer_video.idx == 255 + 3) // full
- {
- if (buffer_video.idx != 0) {
- #warning this is flawed; there is another level of nesting here
- /* set subblock length byte */
- data[0] = buffer_video.idx - 3;
- buffer_poke(&buffer_video, 2, data, 1);
- buffer_flush(&buffer_video);
- }
- data[0] = arg & 0xff;
- data[1] = (arg >> 8) & 0xff;
- buffer_write(&buffer_video, data, 2);
- vdp_addr_next = arg + 1;
-
- /* byte for contiguous subblock length */
- data[0] = 0;
- buffer_write(&buffer_video, data, 1);
- }
-
- /* write data if not a register set */
- if (!(arg & 0x8000)) {
- data[0] = va_arg(va, int);
- logger(_L|L_2, " (>%02X)", data[0]);
- buffer_write(&buffer_video, data, 1);
- vdp_addr_next++;
- }
- logger(_L|L_2, "\n");
- break;
-
- case demo_type_sound:
- /* just send it */
- data[0] = va_arg(va, int);
- logger(_L|L_2, "Sound >%02X\n", data[0]);
- buffer_write(&buffer_sound, data, 1);
- break;
-
- case demo_type_speech:
- /* just send it */
- data[0] = va_arg(va, int);
- logger(_L|L_2, "Speech >%02X", data[0]);
- buffer_write(&buffer_speech, data, 1);
-
- /* this event has a byte of info */
- if (data[0] == demo_speech_adding_byte) {
- data[0] = va_arg(va, int);
- logger(_L|L_2, " (>%02X)", data[0]);
- buffer_write(&buffer_speech, data, 1);
- }
- logger(_L|L_2, "\n");
- break;
-
- default:
- logger(LOG_FATAL, "Unknown event type passed to demo_event (%d)\n",
- type);
- break;
- }
-
- va_end(va);
- return 1;
- }
-