View Page Source

Back to Page
Revision 18 (current)
Edited by feos on 12/10/2016 4:52 PM
Movie file format for [Emulator Resources / Mamerr|MAME-rerecording].      

No support for savestate anchored movies or metadata.

%%TAB Header

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   ¨   

%%TAB Source code

Relevant contents of {{src\emu\inptport.c}}

%%SRC_EMBED 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);
	}
}

%%END_EMBED

%%TAB_END