Movie file format for MAME-rerecording.
No support for savestate anchored movies or metadata.
Actual header is 56 bytes long, then there go frame count and rerecord count, 64 bytes in total.
Byte | Length | Type | Meaning |
---|---|---|---|
0x00 | 8 | string | "MAMETAS\0" signature |
0x08 | 1 | int | Input file major version, defined as 1 |
0x09 | 1 | int | Input file minor version defined as 0 |
0x0A | 2 | null | Unused |
0x0C | 12 | string | Short game name (up to 8 chars) |
0x18 | 24 | string | Build version |
0x30 | 8 | double | Framerate |
0x38 | 4 | int | Frame count |
0x3C | 4 | int | Rerecord count |
Example:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F 00000000 4D 41 4D 45 54 41 53 00 01 00 00 00 64 6B 6F 6E MAMETAS dkon 00000010 67 6A 6F 31 00 00 00 00 30 2E 31 33 39 20 28 44 gjo1 0.139 (D 00000020 65 63 20 31 30 20 32 30 31 36 29 00 00 00 00 00 ec 10 2016) 00000030 C7 BE D9 64 93 4D 4E 40 4B 15 00 00 A8 00 00 00 ǾÙd“MN@K ¨
Relevant contents of
src\emu\inptport.c
#define INP_HEADER_SIZE 56
#define INP_HEADER_MAJVERSION 1
#define INP_HEADER_MINVERSION 0
struct game_driver
{
const char * source_file; /* set this to __FILE__ */
const char * parent; /* if this is a clone, the name of the parent */
const char * name; /* short (8-character) name of the game */
const char * description; /* full name of the game */
const char * year; /* year the game was released */
const char * manufacturer; /* manufacturer of the game */
const machine_config_token *machine_config; /* machine driver tokens */
const input_port_token *ipt; /* pointer to array of input port tokens */
void (*driver_init) (running_machine *machine); /* DRIVER_INIT callback */
const rom_entry * rom; /* pointer to list of ROMs for the game */
const char * compatible_with;
UINT32 flags; /* orientation and other flags; see defines below */
const char * default_layout; /* default internally defined layout */
};
struct _input_port_private
{
/* global state */
UINT8 safe_to_read; /* clear at start; set after state is loaded */
/* types */
input_type_state * typestatelist; /* list of live type states */
input_type_state * type_to_typestate[__ipt_max][MAX_PLAYERS]; /* map from type/player to type state */
/* specific special global input states */
digital_joystick_state joystick_info[MAX_PLAYERS][DIGITAL_JOYSTICKS_PER_PLAYER]; /* joystick states */
/* frame time tracking */
attotime last_frame_time; /* time of the last frame callback */
attoseconds_t last_delta_nsec; /* nanoseconds that passed since the previous callback */
/* playback/record information */
FILE * record_file; /* recording file (NULL if not recording) */
FILE * playback_file; /* playback file (NULL if not recording) */
UINT32 current_frame;
UINT32 total_frames; /* accumulated frames during playback or recording */
UINT32 rerecord_count;
UINT32 bytes_per_frame;
UINT8 movie_header[INP_HEADER_SIZE];
char movie_filename[_MAX_PATH];
UINT32 movie_readonly;
};
static void record_init(running_machine *machine)
{
char filename[_MAX_PATH];
strncpy(filename, options_get_string(machine->options(), OPTION_RECORD),_MAX_PATH);
if (scheduled_record_file[0] != 0) {
strncpy(filename, scheduled_record_file, _MAX_PATH);
strncpy(current_movie_file, scheduled_record_file, _MAX_PATH);
scheduled_record_file[0] = 0;
}
/* if file, open */
if (filename[0] != 0) {
strncpy(machine->input_port_data->movie_filename, filename, _MAX_PATH);
record_open_file(machine, filename);
}
}
static void record_open_file(running_machine *machine,const char* filename)
{
input_port_private *portdata = machine->input_port_data;
set_bytes_per_frame(machine);
/* open the record file */
portdata->record_file = fopen(filename, "w+b");
if (!portdata->record_file) {
mame_printf_info("Failed to open file %s for recording.\n", filename);
return;
}
/* fill in the header */
memset(portdata->movie_header, 0, sizeof(portdata->movie_header));
memcpy(portdata->movie_header, "MAMETAS\0", 8);
portdata->movie_header[0x08] = INP_HEADER_MAJVERSION;
portdata->movie_header[0x09] = INP_HEADER_MINVERSION;
strcpy((char *)portdata->movie_header + 0x0c, machine->gamedrv->name);
sprintf((char *)portdata->movie_header + 0x18, "%s", build_version);
// initialize movie
movie.pointer = movie.buffer;
movie.size = 0;
portdata->movie_readonly = 0;
}
static void record_end(running_machine *machine, const char *message)
{
input_port_private *portdata = machine->input_port_data;
/* only applies if we have a live file */
if (portdata->record_file != NULL)
{
int movie_buffer_length = movie.pointer - movie.buffer;
int frame = movie_buffer_length / portdata->bytes_per_frame;
double framerate = ATTOSECONDS_TO_HZ(machine->primary_screen->frame_period().attoseconds);
memcpy(portdata->movie_header + 0x30, &framerate, sizeof(double));
fwrite(portdata->movie_header, 1, sizeof(portdata->movie_header), portdata->record_file);
fwrite(&frame, 1, sizeof(frame), portdata->record_file);
fwrite(&portdata->rerecord_count, 1, sizeof(portdata->rerecord_count), portdata->record_file);
fwrite(movie.buffer, 1, movie_buffer_length, portdata->record_file);
/* close the file */
fclose(portdata->record_file);
portdata->record_file = NULL;
/* pop a message */
if (message != NULL)
popmessage("Recording Ended\nReason: %s", message);
}
}