From 8b4e5018a9b50d55e99414246ff154178ae57233 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Sat, 19 Mar 2022 14:10:30 +0000 Subject: [PATCH] Add a new program `dvd-info' to report on interesting things. I'm getting deeper in here, I know. --- .gitignore | 1 + Makefile | 3 + dvd-info.c | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+) create mode 100644 dvd-info.c diff --git a/.gitignore b/.gitignore index 96a7a15..4ebfbf3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ /dvd-cache-keys /dvd-check-keys /dvd-id +/dvd-info /dvd-sector-copy diff --git a/Makefile b/Makefile index f68c312..f931c76 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,9 @@ dvd-check-keys_LIBS = -ldvdcss PROGS += chkdvdimg chkdvdimg_SRCS = chkdvdimg.c lib.c multiprogress.c +PROGS += dvd-info +dvd-info_SRCS = dvd-info.c lib.c multiprogress.c + SCRIPTS += dvdrip SCRIPTS += dvdrip-upload SCRIPTS += dvdrip-monitor diff --git a/dvd-info.c b/dvd-info.c new file mode 100644 index 0000000..a531607 --- /dev/null +++ b/dvd-info.c @@ -0,0 +1,238 @@ +#include "lib.h" + +#include + +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); +} -- 2.11.0