--- /dev/null
+#include "lib.h"
+
+#include <dvdread/ifo_print.h>
+
+static void usage(FILE *fp)
+{
+ fprintf(fp, "usage: %s DEVICE QUERY ...\n"
+ "QUERY ::= dumpvmg | dumpvts:I | dumpall |\n"
+ "\ttitles | chapters:I | duration:I[.C[-[D]]]\n",
+ prog);
+}
+
+static const char *dvdfn;
+static dvd_reader_t *dvd;
+static ifo_handle_t *vmgi;
+
+static void read_tt_srpt(void)
+ { if (!ifoRead_TT_SRPT(vmgi)) bail("failed to read title search table"); }
+
+static int nchapters(int ti)
+ { read_tt_srpt(); return (vmgi->tt_srpt->title[ti - 1].nr_of_ptts); }
+
+static unsigned decode_bcd_byte(unsigned bcd)
+{
+ unsigned i, j, k, n;
+
+ for (i = 0, j = 0, k = 1; j < 2; j++, k *= 10, bcd >>= 4) {
+ n = bcd&0x0f; if (n >= 10) { moan("invalid bcd"); return (0); }
+ i += k*n;
+ }
+ return (i);
+}
+
+static double convert_time(dvd_time_t t)
+{
+ double tt;
+
+ tt = 3600*decode_bcd_byte(t.hour) +
+ 60*decode_bcd_byte(t.minute) +
+ decode_bcd_byte(t.second);
+ switch (t.frame_u&0xc0) {
+ case 0x40: tt += (t.frame_u&0x3f)/25; break;
+ case 0xc0: tt += (t.frame_u&0x3f)/30; break;
+ default: moan("bad frame count 0x%02x", t.frame_u); break;
+ }
+ return (tt);
+}
+
+static const pgc_t *find_pgc(const ifo_handle_t *vtsi,
+ unsigned vts, unsigned pgc)
+{
+ const pgcit_t *pgcit = vtsi->vts_pgcit;
+
+ if (pgc > pgcit->nr_of_pgci_srp)
+ bail("resolved pgc %u > %"PRIu16" in vts %u pgc search table",
+ pgc, pgcit->nr_of_pgci_srp, vts);
+ return (pgcit->pgci_srp[pgc - 1].pgc);
+}
+
+static void resolve_chapter(const ifo_handle_t *vtsi,
+ unsigned vts, unsigned ttn, unsigned ch,
+ unsigned *pgc_out, unsigned *pg_out,
+ unsigned *locell_out, unsigned *hicell_out)
+{
+ unsigned pgc, pg;
+ const vts_ptt_srpt_t *pttsrpt;
+ const ttu_t *ttu;
+ const ptt_info_t *ptti;
+ const pgc_t *pgci;
+ const pgc_program_map_t *pgmap;
+
+ pttsrpt = vtsi->vts_ptt_srpt;
+
+ if (ttn > pttsrpt->nr_of_srpts)
+ bail("title number %u > %"PRIu16" in vts %u ptt search table",
+ ttn, pttsrpt->nr_of_srpts, vts);
+ ttu = &pttsrpt->title[ttn - 1];
+
+ if (ch > ttu->nr_of_ptts)
+ bail("chapter number %u > %"PRIu16" in vts %u title %u ptt search table",
+ ttn, ttu->nr_of_ptts, vts, ttn);
+ ptti = &ttu->ptt[ch - 1];
+ pgc = ptti->pgcn; if (pgc_out) *pgc_out = pgc;
+ pg = ptti->pgn; if (pg_out) *pg_out = pg;
+
+ if (locell_out || hicell_out) {
+ pgci = find_pgc(vtsi, vts, pgc);
+ if (pg > pgci->nr_of_programs)
+ bail("resolved pg %u > %"PRIu16" in vts %u pgc %u",
+ pg, pgci->nr_of_programs, vts, pgc);
+ pgmap = &pgci->program_map[pg - 1];
+
+ if (locell_out) *locell_out = pgmap[0];
+ if (hicell_out) {
+ if (pg < pgci->nr_of_programs) *hicell_out = pgmap[1] - 1;
+ else *hicell_out = pgci->nr_of_cells;
+ }
+ }
+}
+
+static void show_duration(int ti, int loch, int hich)
+{
+ ifo_handle_t *vtsi;
+ const pgcit_t *pgcit;
+ const pgci_srp_t *pgcsrp;
+ const pgc_t *pgci;
+ const cell_playback_t *celli;
+ unsigned pgc, locell, hicell;
+ int vts, ttn;
+ double t;
+ unsigned i;
+
+ vts = vmgi->tt_srpt->title[ti - 1].title_set_nr;
+ ttn = vmgi->tt_srpt->title[ti - 1].vts_ttn;
+
+ vtsi = ifoOpenVTSI(dvd, vts);
+ if (!vtsi || !ifoRead_VTS_PTT_SRPT(vtsi) || !ifoRead_PGCIT(vtsi))
+ bail("failed to open vtsi for `%s' titleset %d", dvdfn, vts);
+
+ if (loch == 1 && hich == -1) {
+ pgcit = vtsi->vts_pgcit;
+ for (i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ pgcsrp = &pgcit->pgci_srp[i];
+ if (pgcsrp->entry_id == (0x80 | ttn)) goto found_pgc;
+ }
+ bail("failed to find pgc for `%s' title %d", dvdfn, ti);
+ found_pgc:
+ t = convert_time(pgcsrp->pgc->playback_time);
+ } else {
+ if (loch == hich)
+ resolve_chapter(vtsi, vts, ttn, loch, &pgc, 0, &locell, &hicell);
+ else {
+ resolve_chapter(vtsi, vts, ttn, loch, &pgc, 0, &locell, 0);
+ resolve_chapter(vtsi, vts, ttn, hich, 0, 0, 0, &hicell);
+ }
+
+ pgci = find_pgc(vtsi, vts, pgc); t = 0;
+ for (i = locell - 1; i < hicell; i++) {
+ celli = &pgci->cell_playback[i];
+ if (celli->block_type == BLOCK_TYPE_NONE ||
+ celli->block_mode == BLOCK_MODE_FIRST_CELL)
+ t += convert_time(celli->playback_time);
+ }
+ }
+
+ printf("%g", t);
+ ifoClose(vtsi);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, j, ti, nch, loch, hich, opt;
+ unsigned f = 0;
+ const char *p;
+#define f_bogus 1u
+#define f_any 256u
+
+ set_prog(argv[0]);
+ for (;;) {
+ opt = getopt(argc, argv, "h"); if (opt < 0) break;
+ switch (opt) {
+ case 'h': usage(stderr); exit(0);
+ default: f |= f_bogus; break;
+ }
+ }
+ if (argc - optind < 2) f |= f_bogus;
+ if (f&f_bogus) { usage(stderr); exit(2); }
+ setlocale(LC_ALL, "");
+ progress_init(&progress);
+ dvdfn = argv[optind]; open_dvd(dvdfn, 0, &dvd);
+ vmgi = ifoOpenVMGI(dvd);
+ if (!vmgi) bail("failed to open vmgi for `%s'", dvdfn);
+
+ f &= ~f_any;
+ for (i = optind + 1; i < argc; i++) {
+ p = argv[i];
+#define SKIP_PREFIX(s) \
+ (STRNCMP(p, ==, s ":", sizeof(s)) && (p += sizeof(s), 1))
+ if (STRCMP(p, ==, "dumpvmg")) {
+ if (f&f_any) { fputc('\n', stdout); f &= ~f_any; }
+ ifo_print(dvd, 0);
+ } else if (SKIP_PREFIX("dumpvts")) {
+ ti = parse_int(&p, 0, 1, vmgi->vmgi_mat->vmg_nr_of_title_sets,
+ "vts number");
+ if (f&f_any) { fputc('\n', stdout); f &= ~f_any; }
+ ifo_print(dvd, ti);
+ } else if (STRCMP(p, ==, "dumpall")) {
+ if (f&f_any) { fputc('\n', stdout); f &= ~f_any; }
+ printf(";;;--------------------------------------------------------------------------\n"
+ ";;; Video management infon\n");
+ ifo_print(dvd, 0);
+ for (j = 1; j < vmgi->vmgi_mat->vmg_nr_of_title_sets; j++) {
+ printf("\n"
+ ";;;--------------------------------------------------------------------------\n"
+ ";;; Video titleset %d info\n\n", j);
+ ifo_print(dvd, j);
+ }
+ } else if (STRCMP(p, ==, "titles")) {
+ if (f&f_any) fputc(' ', stdout);
+ read_tt_srpt();
+ printf("%d", vmgi->tt_srpt->nr_of_srpts); f |= f_any;
+ } else if (SKIP_PREFIX("chapters")) {
+ if (f&f_any) fputc(' ', stdout);
+ read_tt_srpt();
+ ti = parse_int(&p, 0, 1, vmgi->tt_srpt->nr_of_srpts, "title number");
+ printf("%"PRIu16"", vmgi->tt_srpt->title[ti - 1].nr_of_ptts);
+ f |= f_any;
+ } else if (SKIP_PREFIX("duration")) {
+ if (f&f_any) fputc(' ', stdout);
+ read_tt_srpt();
+ ti = parse_int(&p, PNF_JUNK, 1, vmgi->tt_srpt->nr_of_srpts,
+ "title number");
+ nch = nchapters(ti);
+ if (*p != '.') {
+ loch = 1; hich = nch;
+ } else {
+ p++; loch = parse_int(&p, PNF_JUNK, 1, nch, "low chapter");
+ if (*p != '-')
+ hich = loch;
+ else {
+ p++;
+ if (!*p) hich = nch;
+ else hich = parse_int(&p, PNF_JUNK, loch, nch, "high chapter");
+ }
+ }
+ if (*p) bail("bad duration request `%s'", argv[i]);
+ show_duration(ti, loch, hich); f |= f_any;
+ } else
+ bail("unknown request `%s'", argv[i]);
+ }
+ if (f&f_any) fputc('\n', stdout);
+
+ ifoClose(vmgi);
+ DVDClose(dvd);
+
+ progress_free(&progress);
+ return (0);
+}