Add a new program `dvd-info' to report on interesting things.
authorMark Wooding <mdw@distorted.org.uk>
Sat, 19 Mar 2022 14:10:30 +0000 (14:10 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 19 Mar 2022 14:10:30 +0000 (14:10 +0000)
I'm getting deeper in here, I know.

.gitignore
Makefile
dvd-info.c [new file with mode: 0644]

index 96a7a15..4ebfbf3 100644 (file)
@@ -5,4 +5,5 @@
 /dvd-cache-keys
 /dvd-check-keys
 /dvd-id
+/dvd-info
 /dvd-sector-copy
index f68c312..f931c76 100644 (file)
--- 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 (file)
index 0000000..a531607
--- /dev/null
@@ -0,0 +1,238 @@
+#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);
+}