Commit | Line | Data |
---|---|---|
8b4e5018 MW |
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); | |
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" | |
190 | ";;; Video management infon\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 = nch; | |
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 = nch; | |
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 | } |