Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/dvdrip
[dvdrip] / dvd-info.c
CommitLineData
8b4e5018
MW
1#include "lib.h"
2
3#include <dvdread/ifo_print.h>
4
5static 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
13static const char *dvdfn;
14static dvd_reader_t *dvd;
15static ifo_handle_t *vmgi;
16
17static void read_tt_srpt(void)
18 { if (!ifoRead_TT_SRPT(vmgi)) bail("failed to read title search table"); }
19
20static int nchapters(int ti)
21 { read_tt_srpt(); return (vmgi->tt_srpt->title[ti - 1].nr_of_ptts); }
22
23static 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
34static 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
49static 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
60static 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
101static 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
150int 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);
972381af 170 dvdfn = argv[optind]; open_dvd(dvdfn, O_RDONLY, 0, &dvd);
8b4e5018
MW
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"
75711dd7 190 ";;; Video management info\n\n");
8b4e5018 191 ifo_print(dvd, 0);
568d6646 192 for (j = 1; j <= vmgi->vmgi_mat->vmg_nr_of_title_sets; j++) {
8b4e5018
MW
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 != '.') {
4789962f 215 loch = 1; hich = -1;
8b4e5018
MW
216 } else {
217 p++; loch = parse_int(&p, PNF_JUNK, 1, nch, "low chapter");
218 if (*p != '-')
219 hich = loch;
220 else {
221 p++;
4789962f 222 if (!*p) hich = -1;
8b4e5018
MW
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}