| 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 | } |