home *** CD-ROM | disk | FTP | other *** search
/ ftp.whtech.com / ftp.whtech.com.7z / ftp.whtech.com / emulators / v9t9 / linux / sources / V9t9 / source / demo.c < prev    next >
Encoding:
C/C++ Source or Header  |  2006-10-19  |  7.6 KB  |  343 lines

  1. #include <stdarg.h>
  2.  
  3. #include "v9t9_common.h"
  4. #include "vdp.h"
  5. #include "sound.h"
  6. #include "demo.h"
  7.  
  8. #define _L LOG_DEMO
  9.  
  10. static OSSpec    demo_spec;
  11. static OSRef    demo_ref;
  12. static OSHandle    demo_handle;
  13. static bool        demo_recording;
  14.  
  15. /*
  16.  *    Append data to demo.
  17.  */
  18. static void handle_append(OSHandle *handle, void *data, OSSize size)
  19. {
  20.     OSError err;
  21.  
  22.     if (demo_recording) {
  23.         err = OS_AppendHandle(handle, data, size);
  24.  
  25.         if (err != OS_NOERR) {
  26.             logger(_L|LOG_ERROR|LOG_USER, "Could not add %d bytes to demo (%s)\n",
  27.                    size, OS_GetErrText(err));
  28.             demo_stop_record();
  29.         }
  30.     }
  31. }
  32.  
  33. typedef struct
  34. {
  35.     int    type, idx, max;
  36.     u8 *data;
  37. }    demo_buffer;
  38.  
  39. static demo_buffer    buffer_video, buffer_sound, buffer_speech;
  40. static u16 vdp_addr_next;
  41.  
  42. /*
  43.  *    Initialize buffer for reading or writing.
  44.  */
  45. static void buffer_init(demo_buffer *buffer, int size, int type)
  46. {
  47.     buffer->type = type;
  48.     buffer->idx = 0;
  49.     buffer->max = size;
  50.     if (buffer->data) xfree(buffer->data);
  51.     buffer->data = (u8 *)xmalloc(size);
  52. }
  53.  
  54. /*
  55.  *    Terminate use of a buffer.
  56.  */
  57. static void buffer_term(demo_buffer *buffer)
  58. {
  59.     if (buffer->data)
  60.         xfree(buffer->data);
  61.     memset(buffer, 0, sizeof(demo_buffer));
  62. }
  63.  
  64. /*
  65.  *    Set up a buffer for reading or writing.
  66.  */
  67. static void buffer_setup(demo_buffer *buffer)
  68. {
  69.     buffer->idx = 0;
  70. }
  71.  
  72. /*
  73.  *    Flush a buffer to the file, with a type and length header.
  74.  */
  75. static void buffer_flush(demo_buffer *buffer)
  76. {
  77.     u8 header[4];
  78.  
  79.     // write event type and buffer length
  80.     header[0] = buffer->type;
  81.     header[1] = (buffer->idx & 0xff);
  82.     header[2] = (buffer->idx >> 8) & 0xff;
  83.     handle_append(&demo_handle, (void *)header, 3);
  84.  
  85.     // write data
  86.     handle_append(&demo_handle, (void *)buffer->data, buffer->idx);
  87.  
  88.     buffer->idx = 0;
  89. }
  90.  
  91. /*
  92.  *    Write data to a buffer
  93.  */
  94. static void buffer_write(demo_buffer *buffer, void *data, int len)
  95. {
  96.     if (buffer->idx + len > buffer->max) {
  97.         buffer_flush(buffer);
  98.     }
  99.     memcpy((u8 *)buffer->data + buffer->idx, data, len);
  100.     buffer->idx += len;
  101. }
  102.  
  103. /*
  104.  *    Write data into a buffer
  105.  */
  106. static void buffer_poke(demo_buffer *buffer, int offset, void *data, int len)
  107. {
  108.     if (offset + len > buffer->idx) {
  109.         logger(_L|LOG_FATAL, "buffer_poke:  overrun (%d+%d > %d)\n",
  110.                offset, len, buffer->idx);
  111.     }
  112.     memcpy((u8 *)buffer->data + offset, data, len);
  113. }
  114.  
  115. void demo_init(void)
  116. {
  117.     // these constants were hardcoded in v9t9 6.0 and
  118.     // should probably be kept this way
  119.     buffer_init(&buffer_video, 8192, demo_type_video);
  120.     buffer_init(&buffer_sound, 1024, demo_type_sound);
  121.     buffer_init(&buffer_speech, 512, demo_type_speech);
  122. }
  123.  
  124. void demo_term(void)
  125. {
  126.     buffer_term(&buffer_video);
  127.     buffer_term(&buffer_sound);
  128.     buffer_term(&buffer_speech);
  129. }
  130.  
  131. int demo_start_record(OSSpec *spec)
  132. {
  133.     OSError err;
  134.     int addr;
  135.  
  136.     logger(_L|L_1, "Setting up demo (%s)\n", OS_SpecToString1(spec));
  137.  
  138.     /* Create file first */
  139.     demo_spec = *spec;
  140.     err = OS_Create(spec, &OS_TEXTTYPE);
  141.     if (err != OS_NOERR) {
  142.         logger(_L|LOG_ERROR|LOG_USER, "Could not create demo file '%s' (%s)\n",
  143.                OS_SpecToString1(spec), OS_GetErrText(err));
  144.         return 0;
  145.     }
  146.  
  147.     /* Store all data in a memory handle */
  148.     err = OS_NewHandle(0, &demo_handle);
  149.     if (err != OS_NOERR) {
  150.         logger(_L|LOG_ERROR|LOG_USER, "Could not allocate memory for demo (%s)\n",
  151.                OS_GetErrText(err));
  152.         return 0;
  153.     }
  154.  
  155.     /* Clear buffers */
  156.     buffer_setup(&buffer_video);
  157.     buffer_setup(&buffer_sound);
  158.     buffer_setup(&buffer_speech);
  159.  
  160.     /* Write initial data */
  161.     demo_recording = true;
  162.     stateflag |= ST_DEMOING;
  163.  
  164.     /* Magic header */
  165.     handle_append(&demo_handle, DEMO_MAGIC_HEADER, sizeof(DEMO_MAGIC_HEADER));
  166.  
  167.     /* Write VDP regs */
  168.     for (addr = 0; addr < 8; addr++) {
  169.         demo_record_event(demo_type_video, 
  170.                        0x8000 + (addr << 8) + vdpregs[addr]);
  171.     }
  172.  
  173.     /* Write VDP memory */
  174.     for (addr = 0; addr < 0x4000; addr++) {
  175.         demo_record_event(demo_type_video, 
  176.                        0x4000 + addr, 
  177.                        vdp_mmio_read(addr));
  178.     }
  179.     buffer_flush(&buffer_video);
  180.  
  181.     /* Write sound data */
  182.     for (addr = 0; addr < 4; addr++) {
  183.         u8 base = 0x80 + (addr << 5);
  184.  
  185.         /* tone/noise bytes */
  186.         demo_record_event(demo_type_sound, 
  187.                        sound_voices[addr].operation[OPERATION_FREQUENCY_LO]);
  188.         demo_record_event(demo_type_sound, 
  189.                        sound_voices[addr].operation[OPERATION_FREQUENCY_HI]);
  190.         demo_record_event(demo_type_sound, 
  191.                        sound_voices[addr].operation[OPERATION_ATTENUATION]);
  192.  
  193.         /* volume */
  194.         demo_record_event(demo_type_sound, 
  195.                        base + sound_voices[addr].volume);
  196.     }
  197.     buffer_flush(&buffer_sound);
  198.  
  199.     /* Write speech data */
  200.     demo_record_event(demo_type_speech, demo_speech_terminating);
  201.     buffer_flush(&buffer_speech);
  202.     
  203.     return 1;
  204. }
  205.  
  206. void demo_pause_record(bool pausing)
  207. {
  208.     demo_record_event(demo_type_tick);
  209.     if (pausing)
  210.         stateflag &= ~ST_DEMOING;
  211.     else
  212.         stateflag |= ST_DEMOING;
  213. }
  214.  
  215. void demo_stop_record(void)
  216. {
  217.     OSError err;
  218.     OSRef demo_ref;
  219.     OSSize size;
  220.     void *ptr;
  221.  
  222.     logger(_L|L_1, "Closing demo\n");
  223.     if (demo_recording) {
  224.         logger(_L|L_1, "Closing demo (%s)\n", OS_SpecToString1(&demo_spec));
  225.  
  226.         buffer_flush(&buffer_video);
  227.         buffer_flush(&buffer_sound);
  228.         buffer_flush(&buffer_speech);
  229.  
  230.         demo_recording = false;
  231.  
  232.         if ((err = OS_Open(&demo_spec, OSWrite, &demo_ref)) == OS_NOERR &&
  233.             (err = OS_GetHandleSize(&demo_handle, &size)) == OS_NOERR &&
  234.             (ptr = OS_LockHandle(&demo_handle)) != 0L &&
  235.             (err = OS_Write(demo_ref, (void *)ptr, &size)) == OS_NOERR)
  236.         {
  237.             OS_UnlockHandle(&demo_handle);
  238.             OS_FreeHandle(&demo_handle);
  239.             OS_Close(demo_ref);
  240.         }
  241.         else {
  242.             logger(_L|LOG_USER|LOG_ERROR, "Could not write demo file '%s' (%s)\n",
  243.                    OS_SpecToString1(&demo_spec), OS_GetErrText(err));
  244.         }
  245.         stateflag &= ~ST_DEMOING;
  246.     }
  247. }
  248.  
  249. /*
  250.  *    Main interface to demo saving engine
  251.  */
  252. int demo_record_event(demo_type type, ...)
  253. {
  254.     va_list va;
  255.     int arg;
  256.     u8 data[8];
  257.  
  258.     va_start(va, type);
  259.     switch (type)
  260.     {
  261.     case demo_type_tick:
  262.         /* flush all current data */
  263.         logger(_L|L_1, "Tick\n");
  264.         data[0] = type;
  265.         handle_append(&demo_handle, (void *)data, 1);
  266.         buffer_flush(&buffer_video);
  267.         buffer_flush(&buffer_sound);
  268.         buffer_flush(&buffer_speech);
  269.         break;
  270.  
  271.         /* for these events, buffer */
  272.  
  273.     case demo_type_video:
  274.         /* if address is different than the last one
  275.            plus one, flush the buffer */
  276.         arg = va_arg(va, int);
  277.  
  278.         my_assert(buffer_video.max == 256);
  279.         
  280.         logger(_L|L_2, "Video >%04X", arg);
  281.         if (arg != vdp_addr_next         // contiguous
  282.             || buffer_video.idx == 0     // empty
  283.             || buffer_video.idx == 255 + 3) // full
  284.         {
  285.             if (buffer_video.idx != 0) {
  286. #warning this is flawed; there is another level of nesting here
  287.                 /* set subblock length byte */
  288.                 data[0] = buffer_video.idx - 3;
  289.                 buffer_poke(&buffer_video, 2, data, 1);
  290.                 buffer_flush(&buffer_video);
  291.             }
  292.             data[0] = arg & 0xff;
  293.             data[1] = (arg >> 8) & 0xff;
  294.             buffer_write(&buffer_video, data, 2);
  295.             vdp_addr_next = arg + 1;
  296.  
  297.             /* byte for contiguous subblock length */
  298.             data[0] = 0;
  299.             buffer_write(&buffer_video, data, 1);
  300.         }
  301.  
  302.         /* write data if not a register set */
  303.         if (!(arg & 0x8000)) {
  304.             data[0] = va_arg(va, int);
  305.             logger(_L|L_2, " (>%02X)", data[0]);
  306.             buffer_write(&buffer_video, data, 1);
  307.             vdp_addr_next++;
  308.         }
  309.         logger(_L|L_2, "\n");
  310.         break;
  311.  
  312.     case demo_type_sound:
  313.         /* just send it */
  314.         data[0] = va_arg(va, int);
  315.         logger(_L|L_2, "Sound >%02X\n", data[0]);
  316.         buffer_write(&buffer_sound, data, 1);
  317.         break;
  318.  
  319.     case demo_type_speech:
  320.         /* just send it */
  321.         data[0] = va_arg(va, int);
  322.         logger(_L|L_2, "Speech >%02X", data[0]);
  323.         buffer_write(&buffer_speech, data, 1);
  324.  
  325.         /* this event has a byte of info */
  326.         if (data[0] == demo_speech_adding_byte) {
  327.             data[0] = va_arg(va, int);
  328.             logger(_L|L_2, " (>%02X)", data[0]);
  329.             buffer_write(&buffer_speech, data, 1);
  330.         }
  331.         logger(_L|L_2, "\n");
  332.         break;
  333.  
  334.     default:
  335.         logger(LOG_FATAL, "Unknown event type passed to demo_event (%d)\n",
  336.                type);
  337.         break;
  338.     }
  339.  
  340.     va_end(va);
  341.     return 1;
  342. }
  343.