chiark / gitweb /
Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/dvdrip
[dvdrip] / dvd-info.c
1 #include "lib.h"
2
3 #include <dvdread/ifo_print.h>
4
5 static void usage(FILE *fp)
6 {
7   fprintf(fp, "usage: %s DEVICE QUERY ...\n"
8           "QUERY ::= dumpvmg | dumpvts:I | dumpall |\n"
9           "\ttitles | chapters:I | duration:I[.C[-[D]]]\n",
10           prog);
11 }
12
13 static const char *dvdfn;
14 static dvd_reader_t *dvd;
15 static ifo_handle_t *vmgi;
16
17 static void read_tt_srpt(void)
18   { if (!ifoRead_TT_SRPT(vmgi)) bail("failed to read title search table"); }
19
20 static int nchapters(int ti)
21   { read_tt_srpt(); return (vmgi->tt_srpt->title[ti - 1].nr_of_ptts); }
22
23 static unsigned decode_bcd_byte(unsigned bcd)
24 {
25   unsigned i, j, k, n;
26
27   for (i = 0, j = 0, k = 1; j < 2; j++, k *= 10, bcd >>= 4) {
28     n = bcd&0x0f; if (n >= 10) { moan("invalid bcd"); return (0); }
29     i += k*n;
30   }
31   return (i);
32 }
33
34 static double convert_time(dvd_time_t t)
35 {
36   double tt;
37
38   tt = 3600*decode_bcd_byte(t.hour) +
39          60*decode_bcd_byte(t.minute) +
40             decode_bcd_byte(t.second);
41   switch (t.frame_u&0xc0) {
42     case 0x40: tt += (t.frame_u&0x3f)/25; break;
43     case 0xc0: tt += (t.frame_u&0x3f)/30; break;
44     default: moan("bad frame count 0x%02x", t.frame_u); break;
45   }
46   return (tt);
47 }
48
49 static const pgc_t *find_pgc(const ifo_handle_t *vtsi,
50                              unsigned vts, unsigned pgc)
51 {
52   const pgcit_t *pgcit = vtsi->vts_pgcit;
53
54   if (pgc > pgcit->nr_of_pgci_srp)
55     bail("resolved pgc %u > %"PRIu16" in vts %u pgc search table",
56          pgc, pgcit->nr_of_pgci_srp, vts);
57   return (pgcit->pgci_srp[pgc - 1].pgc);
58 }
59
60 static void resolve_chapter(const ifo_handle_t *vtsi,
61                             unsigned vts, unsigned ttn, unsigned ch,
62                             unsigned *pgc_out, unsigned *pg_out,
63                             unsigned *locell_out, unsigned *hicell_out)
64 {
65   unsigned pgc, pg;
66   const vts_ptt_srpt_t *pttsrpt;
67   const ttu_t *ttu;
68   const ptt_info_t *ptti;
69   const pgc_t *pgci;
70   const pgc_program_map_t *pgmap;
71
72   pttsrpt = vtsi->vts_ptt_srpt;
73
74   if (ttn > pttsrpt->nr_of_srpts)
75     bail("title number %u > %"PRIu16" in vts %u ptt search table",
76          ttn, pttsrpt->nr_of_srpts, vts);
77   ttu = &pttsrpt->title[ttn - 1];
78
79   if (ch > ttu->nr_of_ptts)
80     bail("chapter number %u > %"PRIu16" in vts %u title %u ptt search table",
81          ttn, ttu->nr_of_ptts, vts, ttn);
82   ptti = &ttu->ptt[ch - 1];
83   pgc = ptti->pgcn; if (pgc_out) *pgc_out = pgc;
84   pg = ptti->pgn; if (pg_out) *pg_out = pg;
85
86   if (locell_out || hicell_out) {
87     pgci = find_pgc(vtsi, vts, pgc);
88     if (pg > pgci->nr_of_programs)
89       bail("resolved pg %u > %"PRIu16" in vts %u pgc %u",
90            pg, pgci->nr_of_programs, vts, pgc);
91     pgmap = &pgci->program_map[pg - 1];
92
93     if (locell_out) *locell_out = pgmap[0];
94     if (hicell_out) {
95       if (pg < pgci->nr_of_programs) *hicell_out = pgmap[1] - 1;
96       else *hicell_out = pgci->nr_of_cells;
97     }
98   }
99 }
100
101 static void show_duration(int ti, int loch, int hich)
102 {
103   ifo_handle_t *vtsi;
104   const pgcit_t *pgcit;
105   const pgci_srp_t *pgcsrp;
106   const pgc_t *pgci;
107   const cell_playback_t *celli;
108   unsigned pgc, locell, hicell;
109   int vts, ttn;
110   double t;
111   unsigned i;
112
113   vts = vmgi->tt_srpt->title[ti - 1].title_set_nr;
114   ttn = vmgi->tt_srpt->title[ti - 1].vts_ttn;
115
116   vtsi = ifoOpenVTSI(dvd, vts);
117   if (!vtsi || !ifoRead_VTS_PTT_SRPT(vtsi) || !ifoRead_PGCIT(vtsi))
118     bail("failed to open vtsi for `%s' titleset %d", dvdfn, vts);
119
120   if (loch == 1 && hich == -1) {
121     pgcit = vtsi->vts_pgcit;
122     for (i = 0; i < pgcit->nr_of_pgci_srp; i++) {
123       pgcsrp = &pgcit->pgci_srp[i];
124       if (pgcsrp->entry_id == (0x80 | ttn)) goto found_pgc;
125     }
126     bail("failed to find pgc for `%s' title %d", dvdfn, ti);
127   found_pgc:
128     t = convert_time(pgcsrp->pgc->playback_time);
129   } else {
130     if (loch == hich)
131       resolve_chapter(vtsi, vts, ttn, loch, &pgc, 0, &locell, &hicell);
132     else {
133       resolve_chapter(vtsi, vts, ttn, loch, &pgc, 0, &locell, 0);
134       resolve_chapter(vtsi, vts, ttn, hich, 0, 0, 0, &hicell);
135     }
136
137     pgci = find_pgc(vtsi, vts, pgc); t = 0;
138     for (i = locell - 1; i < hicell; i++) {
139       celli = &pgci->cell_playback[i];
140       if (celli->block_type == BLOCK_TYPE_NONE ||
141           celli->block_mode == BLOCK_MODE_FIRST_CELL)
142         t += convert_time(celli->playback_time);
143     }
144   }
145
146   printf("%g", t);
147   ifoClose(vtsi);
148 }
149
150 int main(int argc, char *argv[])
151 {
152   int i, j, ti, nch, loch, hich, opt;
153   unsigned f = 0;
154   const char *p;
155 #define f_bogus 1u
156 #define f_any 256u
157
158   set_prog(argv[0]);
159   for (;;) {
160     opt = getopt(argc, argv, "h"); if (opt < 0) break;
161     switch (opt) {
162       case 'h': usage(stderr); exit(0);
163       default: f |= f_bogus; break;
164     }
165   }
166   if (argc - optind < 2) f |= f_bogus;
167   if (f&f_bogus) { usage(stderr); exit(2); }
168   setlocale(LC_ALL, "");
169   progress_init(&progress);
170   dvdfn = argv[optind]; open_dvd(dvdfn, O_RDONLY, 0, &dvd);
171   vmgi = ifoOpenVMGI(dvd);
172   if (!vmgi) bail("failed to open vmgi for `%s'", dvdfn);
173
174   f &= ~f_any;
175   for (i = optind + 1; i < argc; i++) {
176     p = argv[i];
177 #define SKIP_PREFIX(s)                                                  \
178         (STRNCMP(p, ==, s ":", sizeof(s)) && (p += sizeof(s), 1))
179     if (STRCMP(p, ==, "dumpvmg")) {
180       if (f&f_any) { fputc('\n', stdout); f &= ~f_any; }
181       ifo_print(dvd, 0);
182     } else if (SKIP_PREFIX("dumpvts")) {
183       ti = parse_int(&p, 0, 1, vmgi->vmgi_mat->vmg_nr_of_title_sets,
184                      "vts number");
185       if (f&f_any) { fputc('\n', stdout); f &= ~f_any; }
186       ifo_print(dvd, ti);
187     } else if (STRCMP(p, ==, "dumpall")) {
188       if (f&f_any) { fputc('\n', stdout); f &= ~f_any; }
189       printf(";;;--------------------------------------------------------------------------\n"
190              ";;; Video management info\n\n");
191       ifo_print(dvd, 0);
192       for (j = 1; j <= vmgi->vmgi_mat->vmg_nr_of_title_sets; j++) {
193         printf("\n"
194                ";;;--------------------------------------------------------------------------\n"
195                ";;; Video titleset %d info\n\n", j);
196         ifo_print(dvd, j);
197       }
198     } else if (STRCMP(p, ==, "titles")) {
199       if (f&f_any) fputc(' ', stdout);
200       read_tt_srpt();
201       printf("%d", vmgi->tt_srpt->nr_of_srpts); f |= f_any;
202     } else if (SKIP_PREFIX("chapters")) {
203       if (f&f_any) fputc(' ', stdout);
204       read_tt_srpt();
205       ti = parse_int(&p, 0, 1, vmgi->tt_srpt->nr_of_srpts, "title number");
206       printf("%"PRIu16"", vmgi->tt_srpt->title[ti - 1].nr_of_ptts);
207       f |= f_any;
208     } else if (SKIP_PREFIX("duration")) {
209       if (f&f_any) fputc(' ', stdout);
210       read_tt_srpt();
211       ti = parse_int(&p, PNF_JUNK, 1, vmgi->tt_srpt->nr_of_srpts,
212                      "title number");
213       nch = nchapters(ti);
214       if (*p != '.') {
215         loch = 1; hich = -1;
216       } else {
217         p++; loch = parse_int(&p, PNF_JUNK, 1, nch, "low chapter");
218         if (*p != '-')
219           hich = loch;
220         else {
221           p++;
222           if (!*p) hich = -1;
223           else hich = parse_int(&p, PNF_JUNK, loch, nch, "high chapter");
224         }
225       }
226       if (*p) bail("bad duration request `%s'", argv[i]);
227       show_duration(ti, loch, hich); f |= f_any;
228     } else
229       bail("unknown request `%s'", argv[i]);
230   }
231   if (f&f_any) fputc('\n', stdout);
232
233   ifoClose(vmgi);
234   DVDClose(dvd);
235
236   progress_free(&progress);
237   return (0);
238 }