3 #include <dvdread/ifo_print.h>
5 static void usage(FILE *fp
)
7 fprintf(fp
, "usage: %s DEVICE QUERY ...\n"
8 "QUERY ::= dumpvmg | dumpvts:I | dumpall |\n"
9 "\ttitles | chapters:I | duration:I[.C[-[D]]]\n",
13 static const char *dvdfn
;
14 static dvd_reader_t
*dvd
;
15 static ifo_handle_t
*vmgi
;
17 static void read_tt_srpt(void)
18 { if (!ifoRead_TT_SRPT(vmgi
)) bail("failed to read title search table"); }
20 static int nchapters(int ti
)
21 { read_tt_srpt(); return (vmgi
->tt_srpt
->title
[ti
- 1].nr_of_ptts
); }
23 static unsigned decode_bcd_byte(unsigned bcd
)
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); }
34 static double convert_time(dvd_time_t t
)
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;
49 static const pgc_t
*find_pgc(const ifo_handle_t
*vtsi
,
50 unsigned vts
, unsigned pgc
)
52 const pgcit_t
*pgcit
= vtsi
->vts_pgcit
;
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
);
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
)
66 const vts_ptt_srpt_t
*pttsrpt
;
68 const ptt_info_t
*ptti
;
70 const pgc_program_map_t
*pgmap
;
72 pttsrpt
= vtsi
->vts_ptt_srpt
;
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];
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
;
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];
93 if (locell_out
) *locell_out
= pgmap
[0];
95 if (pg
< pgci
->nr_of_programs
) *hicell_out
= pgmap
[1] - 1;
96 else *hicell_out
= pgci
->nr_of_cells
;
101 static void show_duration(int ti
, int loch
, int hich
)
104 const pgcit_t
*pgcit
;
105 const pgci_srp_t
*pgcsrp
;
107 const cell_playback_t
*celli
;
108 unsigned pgc
, locell
, hicell
;
113 vts
= vmgi
->tt_srpt
->title
[ti
- 1].title_set_nr
;
114 ttn
= vmgi
->tt_srpt
->title
[ti
- 1].vts_ttn
;
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
);
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
;
126 bail("failed to find pgc for `%s' title %d", dvdfn
, ti
);
128 t
= convert_time(pgcsrp
->pgc
->playback_time
);
131 resolve_chapter(vtsi
, vts
, ttn
, loch
, &pgc
, 0, &locell
, &hicell
);
133 resolve_chapter(vtsi
, vts
, ttn
, loch
, &pgc
, 0, &locell
, 0);
134 resolve_chapter(vtsi
, vts
, ttn
, hich
, 0, 0, 0, &hicell
);
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
);
150 int main(int argc
, char *argv
[])
152 int i
, j
, ti
, nch
, loch
, hich
, opt
;
160 opt
= getopt(argc
, argv
, "h"); if (opt
< 0) break;
162 case 'h': usage(stderr
); exit(0);
163 default: f
|= f_bogus
; break;
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
, 0, &dvd
);
171 vmgi
= ifoOpenVMGI(dvd
);
172 if (!vmgi
) bail("failed to open vmgi for `%s'", dvdfn
);
175 for (i
= optind
+ 1; i
< argc
; 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
; }
182 } else if (SKIP_PREFIX("dumpvts")) {
183 ti
= parse_int(&p
, 0, 1, vmgi
->vmgi_mat
->vmg_nr_of_title_sets
,
185 if (f
&f_any
) { fputc('\n', stdout
); f
&= ~f_any
; }
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");
192 for (j
= 1; j
< vmgi
->vmgi_mat
->vmg_nr_of_title_sets
; j
++) {
194 ";;;--------------------------------------------------------------------------\n"
195 ";;; Video titleset %d info\n\n", j
);
198 } else if (STRCMP(p
, ==, "titles")) {
199 if (f
&f_any
) fputc(' ', stdout
);
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
);
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
);
208 } else if (SKIP_PREFIX("duration")) {
209 if (f
&f_any
) fputc(' ', stdout
);
211 ti
= parse_int(&p
, PNF_JUNK
, 1, vmgi
->tt_srpt
->nr_of_srpts
,
215 loch
= 1; hich
= nch
;
217 p
++; loch
= parse_int(&p
, PNF_JUNK
, 1, nch
, "low chapter");
223 else hich
= parse_int(&p
, PNF_JUNK
, loch
, nch
, "high chapter");
226 if (*p
) bail("bad duration request `%s'", argv
[i
]);
227 show_duration(ti
, loch
, hich
); f
|= f_any
;
229 bail("unknown request `%s'", argv
[i
]);
231 if (f
&f_any
) fputc('\n', stdout
);
236 progress_free(&progress
);