summaryrefslogtreecommitdiffstatshomepage
path: root/src/tools/makemeta.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/makemeta.c')
-rw-r--r--src/tools/makemeta.c587
1 files changed, 587 insertions, 0 deletions
diff --git a/src/tools/makemeta.c b/src/tools/makemeta.c
new file mode 100644
index 00000000000..3b01a5ab786
--- /dev/null
+++ b/src/tools/makemeta.c
@@ -0,0 +1,587 @@
+/***************************************************************************
+
+ makemeta.c
+
+ Laserdisc metadata generator.
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+****************************************************************************
+
+ Metadata format (12 bytes/field):
+
+ Offset Description
+ ------ ------------------------------------------
+ 0 = version (currently 2)
+ 1 = internal flags:
+ bit 0 = previous field is the same frame
+ bit 1 = next field is the same frame
+ 2 = white flag
+ 3-5 = line 16 Philips code
+ 6-8 = line 17 Philips code
+ 9-11 = line 18 Philips code
+
+***************************************************************************/
+
+#include <stdio.h>
+#include <ctype.h>
+#include "aviio.h"
+#include "bitmap.h"
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define INSERT_FRAME_CODE 0xff000000
+#define INSERT_FRAME_CODE_INC 0xff000001
+#define INVALID_CODE 0xffffffff
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _pattern_data pattern_data;
+struct _pattern_data
+{
+ pattern_data *next;
+ UINT32 line16, line17, line18;
+ int white;
+};
+
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ parse_line - parse a Philips code from a
+ line of video data
+-------------------------------------------------*/
+
+static int parse_line(bitmap_t *bitmap, int line, int expected_bits, UINT8 *result)
+{
+ const UINT16 *source = BITMAP_ADDR16(bitmap, line, 0);
+ int x, edges = 0, bits = 0;
+ int minwidth = 1000;
+ UINT8 bit[720];
+ int edge[720];
+ int error = 0;
+
+ /* clamp expected bits */
+ expected_bits *= 2;
+ if (expected_bits > ARRAY_LENGTH(edge) - 1)
+ expected_bits = ARRAY_LENGTH(edge) - 1;
+
+ /* find the edges in the line */
+ for (x = 1; x < bitmap->width && edges < ARRAY_LENGTH(edge); x++)
+ if (source[x] >= 0xc000 && source[x - 1] < 0xc000)
+ edge[edges++] = x;
+ else if (source[x] <= 0x4000 && source[x - 1] > 0x4000)
+ edge[edges++] = x;
+
+ /* find the minimum width */
+ for (x = 1; x < edges; x++)
+ {
+ int width = edge[x] - edge[x - 1];
+ if (width > 3 && width < minwidth)
+ minwidth = edge[x] - edge[x - 1];
+ }
+
+ /* now generate the bits */
+ for (x = 1; x < edges; x++)
+ {
+ int width = edge[x] - edge[x - 1];
+ if (width > 3)
+ {
+ int count = (width > 3 * minwidth / 2) ? 2 : 1;
+ while (count--)
+ bit[bits++] = (source[edge[x - 1]] >= 0x8000) ? 1 : 0;
+ }
+ }
+
+ /* look for improperly paired bits in the sequence */
+ if (bits < expected_bits)
+ {
+ /* look for two bits in a row of the same type on an even boundary */
+ for (x = 0; x < bits; x += 2)
+ if (bit[x] == bit[x + 1])
+ break;
+
+ /* if we got something wrong, assume we're missing an opening 0 bit */
+ if (x < bits)
+ {
+ memmove(&bit[1], &bit[0], bits);
+ bit[0] = 0;
+ bits++;
+ }
+ }
+
+ /* trailing bits are 0 */
+ while (bits < expected_bits)
+ bit[bits++] = 0;
+
+ /* output */
+ for (x = 0; x < MIN(bits, expected_bits); x += 2)
+ {
+ static const UINT8 trans[4] = { 0x80, 1, 0, 0xff };
+ result[x/2] = trans[(bit[x] << 1) | bit[x + 1]];
+ if (result[x/2] > 1)
+ error++;
+ }
+ return error ? -(bits / 2) : (bits / 2);
+}
+
+
+/*-------------------------------------------------
+ output_meta - output a line of metadata
+-------------------------------------------------*/
+
+static void output_meta(UINT8 flags, UINT8 white, UINT32 line12, UINT32 line13, UINT32 line14, UINT32 framenum, UINT32 chapternum)
+{
+ /* start with the raw metadata, followed by a comment */
+ printf("02%02X%02X%06X%06X%06X ; ",
+ flags, white, line12, line13, line14);
+
+ /* separate comments for leadin/leadout */
+ if (line13 == 0x88ffff)
+ printf("leadin\n");
+ else if (line13 == 0x80eeee)
+ printf("leadout\n");
+
+ /* otherwise, display the frame and chapter, and indicate white flag/stop code */
+ else
+ {
+ printf("frame %05x ch %02x", framenum, chapternum);
+ if (white)
+ printf(" (white)");
+ if (line12 == 0x82cfff)
+ printf(" (stop)");
+ printf("\n");
+ }
+}
+
+
+/*-------------------------------------------------
+ generate_from_avi - generate the data from
+ an AVI file
+-------------------------------------------------*/
+
+static int generate_from_avi(const char *aviname)
+{
+ UINT32 line12 = 0, line13 = 0, line14 = 0, framenum = 0, chapternum = 0;
+ const avi_movie_info *info;
+ bitmap_t *bitmap;
+ avi_error avierr;
+ avi_file *avi;
+ int white = 0;
+ int frame;
+
+ /* open the file */
+ avierr = avi_open(aviname, &avi);
+ if (avierr != AVIERR_NONE)
+ {
+ fprintf(stderr, "Error opening AVI file: %s\n", avi_error_string(avierr));
+ return 1;
+ }
+
+ /* extract movie info */
+ info = avi_get_movie_info(avi);
+ fprintf(stderr, "%dx%d - %d frames total\n", info->video_width, info->video_height, info->video_numsamples);
+ if (info->video_height != 39)
+ {
+ fprintf(stderr, "Unknown VANC capture format: expected 39 rows\n");
+ return 1;
+ }
+
+ /* allocate a bitmap to hold it */
+ bitmap = bitmap_alloc(info->video_width, info->video_height, BITMAP_FORMAT_YUY16);
+ if (bitmap == NULL)
+ {
+ fprintf(stderr, "Out of memory allocating %dx%d bitmap\n", info->video_width, info->video_height);
+ return 1;
+ }
+
+ /* loop over frames */
+ for (frame = 0; frame < info->video_numsamples; frame++)
+ {
+ int field;
+ UINT8 bits[24];
+
+ /* read the frame */
+ avierr = avi_read_video_frame_yuy16(avi, frame, bitmap);
+ if (avierr != AVIERR_NONE)
+ {
+ fprintf(stderr, "Error reading AVI frame %d: %s\n", frame, avi_error_string(avierr));
+ break;
+ }
+
+ /* loop over two fields */
+ for (field = 0; field < 2; field++)
+ {
+ int prevwhite = white;
+ int i;
+
+ /* line 7 contains the white flag */
+ white = 0;
+ if (*BITMAP_ADDR16(bitmap, 20 * field + 7, bitmap->width / 2) > 0x8000)
+ white = 1;
+
+ /* output metadata for *previous* field */
+ if (frame > 0 || field > 0)
+ {
+ int flags = 0;
+
+ if (!prevwhite) flags |= 0x01;
+ if (!white) flags |= 0x02;
+ output_meta(flags, prevwhite, line12, line13, line14, framenum, chapternum);
+ }
+
+ /* line 12 contains stop code and other interesting bits */
+ line12 = 0;
+ if (parse_line(bitmap, 20 * field + 12, 24, bits) == 24)
+ for (i = 0; i < 24; i++)
+ line12 = (line12 << 1) | bits[i];
+
+ /* line 13 and 14 contain frame/chapter/lead in/out encodings */
+ line13 = 0;
+ if (parse_line(bitmap, 20 * field + 13, 24, bits) == 24)
+ for (i = 0; i < 24; i++)
+ line13 = (line13 << 1) | bits[i];
+
+ line14 = 0;
+ if (parse_line(bitmap, 20 * field + 14, 24, bits) == 24)
+ for (i = 0; i < 24; i++)
+ line14 = (line14 << 1) | bits[i];
+
+ /* the two lines must match */
+// if (line13 != 0 && line14 != 0 && line13 != line14)
+// line13 = line14 = 0;
+
+ /* is this a frame number? */
+ if ((line13 & 0xf00000) == 0xf00000)
+ framenum = line13 & 0x7ffff;
+ if ((line13 & 0xf00fff) == 0x800ddd)
+ chapternum = (line13 >> 12) & 0x7f;
+ }
+ }
+
+ /* output metadata for *previous* field */
+ {
+ int flags = 0;
+
+ if (!white) flags |= 0x01;
+ output_meta(flags, white, line12, line13, line14, framenum, chapternum);
+ }
+
+ bitmap_free(bitmap);
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ parse_philips_code - parse a single Philips
+ code from a string, stopping at the given
+ end characters
+-------------------------------------------------*/
+
+static UINT32 parse_philips_code(char **argptr, const char *endchars)
+{
+ char *arg = *argptr;
+ UINT32 value = 0;
+
+ /* look for special chars first */
+ if (*arg == '+')
+ {
+ *argptr = arg + 1;
+ return INSERT_FRAME_CODE_INC;
+ }
+ else if (*arg == '@')
+ {
+ *argptr = arg + 1;
+ return INSERT_FRAME_CODE;
+ }
+
+ /* parse the rest as hex digits */
+ for ( ; *arg != 0 && strchr(endchars, *arg) == NULL; arg++)
+ {
+ if (*arg >= '0' && *arg <= '9')
+ value = (value << 4) + (*arg - '0');
+ else if (*arg >= 'a' && *arg <= 'f')
+ value = (value << 4) + 10 + (*arg - 'a');
+ else if (*arg >= 'A' && *arg <= 'F')
+ value = (value << 4) + 10 + (*arg - 'A');
+ else
+ return INVALID_CODE;
+ }
+
+ /* if we're too big, we're invalid */
+ if (value > 0xffffff)
+ return INVALID_CODE;
+
+ *argptr = arg;
+ return value;
+}
+
+
+/*-------------------------------------------------
+ parse_pattern - parse a pattern into a series
+ of pattern_data structs
+-------------------------------------------------*/
+
+pattern_data *parse_pattern(char *arg, int *countptr)
+{
+ pattern_data *head = NULL;
+ pattern_data **tailptr = &head;
+ int count = 0;
+
+ /* first parse the count */
+ for ( ; *arg != 0 && *arg != '*'; arg++)
+ {
+ if (!isdigit(*arg))
+ return NULL;
+ count = count * 10 + (*arg - '0');
+ }
+ if (*arg == 0)
+ return NULL;
+ arg++;
+ *countptr = count;
+
+ /* loop until we hit the end */
+ while (*arg != 0)
+ {
+ pattern_data *pat;
+
+ /* allocate a new structure */
+ pat = malloc(sizeof(*pat));
+ if (pat == NULL)
+ return NULL;
+ memset(pat, 0, sizeof(*pat));
+
+ /* bang at the beginning means white flag */
+ if (*arg == '!')
+ {
+ arg++;
+ pat->white = 1;
+ }
+
+ /* parse line16 until we hit a period or comma */
+ pat->line16 = parse_philips_code(&arg, ".,");
+ if (pat->line16 == INVALID_CODE)
+ return NULL;
+ if (*arg != '.' && *arg != ',' && *arg != 0)
+ return NULL;
+ if (*arg == '.')
+ arg++;
+
+ /* parse line17 until we hit a period */
+ pat->line17 = parse_philips_code(&arg, ".,");
+ if (pat->line17 == INVALID_CODE)
+ return NULL;
+ if (*arg != '.' && *arg != ',' && *arg != 0)
+ return NULL;
+ if (*arg == '.')
+ arg++;
+
+ /* parse line18 until we hit a comma */
+ pat->line18 = parse_philips_code(&arg, ",");
+ if (pat->line18 == INVALID_CODE)
+ return NULL;
+ if (*arg != ',' && *arg != 0)
+ return NULL;
+ if (*arg == ',')
+ arg++;
+
+ /* append to the end */
+ *tailptr = pat;
+ tailptr = &pat->next;
+ }
+
+ return head;
+}
+
+
+/*-------------------------------------------------
+ generate_from_pattern - generate metadata
+ from a pattern
+-------------------------------------------------*/
+
+static int generate_from_pattern(pattern_data *pattern, int patcount)
+{
+ pattern_data *curpat = pattern;
+ int framenum = 0, bcdframenum = 0;
+
+ /* loop until we exceed the pattern frame count */
+ while (1)
+ {
+ UINT32 line16, line17, line18;
+ int flags = 0;
+
+ /* handle special codes for line 16 */
+ line16 = curpat->line16;
+ if (line16 == INSERT_FRAME_CODE_INC)
+ {
+ framenum++;
+ bcdframenum = (((framenum / 10000) % 10) << 16) | (((framenum / 1000) % 10) << 12) | (((framenum / 100) % 10) << 8) | (((framenum / 10) % 10) << 4) | (framenum % 10);
+ }
+ if (line16 == INSERT_FRAME_CODE || line16 == INSERT_FRAME_CODE_INC)
+ line16 = 0xf80000 | bcdframenum;
+
+ /* handle special codes for line 17 */
+ line17 = curpat->line17;
+ if (line17 == INSERT_FRAME_CODE_INC)
+ {
+ framenum++;
+ bcdframenum = (((framenum / 10000) % 10) << 16) | (((framenum / 1000) % 10) << 12) | (((framenum / 100) % 10) << 8) | (((framenum / 10) % 10) << 4) | (framenum % 10);
+ }
+ if (line17 == INSERT_FRAME_CODE || line17 == INSERT_FRAME_CODE_INC)
+ line17 = 0xf80000 | bcdframenum;
+
+ /* handle special codes for line 18 */
+ line18 = curpat->line18;
+ if (line18 == INSERT_FRAME_CODE_INC)
+ {
+ framenum++;
+ bcdframenum = (((framenum / 10000) % 10) << 16) | (((framenum / 1000) % 10) << 12) | (((framenum / 100) % 10) << 8) | (((framenum / 10) % 10) << 4) | (framenum % 10);
+ }
+ if (line18 == INSERT_FRAME_CODE || line18 == INSERT_FRAME_CODE_INC)
+ line18 = 0xf80000 | bcdframenum;
+
+ /* bail if we passed the end */
+ if (framenum > patcount)
+ return 0;
+
+ /* if we don't have a white flag, the previous frame must match us */
+ if (!curpat->white) flags |= 0x01;
+
+ /* advance to the next pattern piece */
+ curpat = curpat->next;
+ if (curpat == NULL)
+ curpat = pattern;
+
+ /* if the new field doesn't have a white flag, it must match our current frame */
+ if (!curpat->white) flags |= 0x02;
+
+ /* output the result */
+ output_meta(flags, curpat->white, line16, line17, line18, bcdframenum, 0);
+ }
+
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ usage - display program usage
+-------------------------------------------------*/
+
+static int usage(void)
+{
+ fprintf(stderr, "Usage: \n");
+ fprintf(stderr, " makemeta [avifile.avi] [<option> [<option> [...]]]\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -leadin <count> -- prepend <count> fields of leadin codes\n");
+ fprintf(stderr, " -leadout <count> -- append <count> fields of leadout codes\n");
+ fprintf(stderr, " -pattern <frames>*<pat0>[,<pat1>[,...]] -- repeat Philips code pattern\n");
+ fprintf(stderr, " in place of AVI. <pat0>, <pat1>, etc. are of the form: \n");
+ fprintf(stderr, " [!]A[.B[.C]][,<pattern>] where A,B,C are either hexadecimal\n");
+ fprintf(stderr, " values one of these special codes:\n");
+ fprintf(stderr, " ! -- at the start of a pattern sets the white flag\n");
+ fprintf(stderr, " + -- increments frame number and inserts frame code\n");
+ fprintf(stderr, " @ -- inserts repeat of most recent frame code\n");
+ fprintf(stderr, " non-present values are assumed to be 0\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Examples:\n");
+ fprintf(stderr, " makemeta -pattern 54000*!8da01e.+.@,8da01e,!8da01e.+.@,8da01e,8da01e\n");
+ fprintf(stderr, " generates the following pattern of Philips codes:\n");
+ fprintf(stderr, " 8da01e f80001 f80001 (white)\n");
+ fprintf(stderr, " 8da01e 000000 000000\n");
+ fprintf(stderr, " 8da01e f80002 f80002 (white)\n");
+ fprintf(stderr, " 8da01e 000000 000000\n");
+ fprintf(stderr, " 8da01e 000000 000000\n");
+ fprintf(stderr, " <repeat until frame is 54000>\n");
+ return 1;
+}
+
+
+/*-------------------------------------------------
+ main - main entry point
+-------------------------------------------------*/
+
+int main(int argc, char *argv[])
+{
+ int leadin = 0, leadout = 0;
+ const char *avifile = NULL;
+ pattern_data *pattern = NULL;
+ int patcount = 0;
+ int arg;
+ int i;
+
+ /* iterate over arguments */
+ for (arg = 1; arg < argc; arg++)
+ {
+ /* assume anything without a - is a filename */
+ if (argv[arg][0] != '-')
+ {
+ if (avifile != NULL || pattern != NULL)
+ return usage();
+ avifile = argv[arg];
+ }
+
+ /* look for options */
+ else if (strcmp(argv[arg], "-leadin") == 0)
+ {
+ if (++arg >= argc)
+ return usage();
+ leadin = atoi(argv[arg]);
+ }
+ else if (strcmp(argv[arg], "-leadout") == 0)
+ {
+ if (++arg >= argc)
+ return usage();
+ leadout = atoi(argv[arg]);
+ }
+ else if (strcmp(argv[arg], "-pattern") == 0)
+ {
+ if (avifile != NULL || pattern != NULL)
+ return usage();
+ if (++arg >= argc)
+ return usage();
+ pattern = parse_pattern(argv[arg], &patcount);
+ if (pattern == NULL)
+ return usage();
+ }
+ }
+
+ /* must have an AVI file or a pattern */
+ if (avifile == NULL && pattern == NULL)
+ return usage();
+
+ /* output header and leadin */
+ printf("chdmeta 12\n");
+ for (i = 0; i < leadin; i++)
+ {
+ int flags = (i == 0) ? 0x02 : (i == leadin - 1) ? 0x01 : 0x03;
+ printf("02%02X%02X00000088FFFF88FFFF\n", flags, (i % 2));
+ }
+
+ /* if we got a file, output it */
+ if (avifile != NULL)
+ generate_from_avi(avifile);
+ else if (pattern != NULL)
+ generate_from_pattern(pattern, patcount);
+
+ /* output leadout */
+ for (i = 0; i < leadout; i++)
+ {
+ int flags = (i == 0) ? 0x02 : (i == leadout - 1) ? 0x01 : 0x03;
+ printf("02%02X%02X00000080EEEE80EEEE\n", flags, (i % 2));
+ }
+
+ return 0;
+}