1 /*
2 * lsdvd.c
3 *
4 * DVD info lister
5 *
6 * Copyright (C) 2003 EFF
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation;
11 *
12 * 2003 by Chris Phillips
13 * 2003-04-19 Cleanups get_title_name, added dvdtime2msec, added helper macros,
14 * output info structures in form of a Perl module, by Henk Vergonet.
15 */
16 #include <stdint.h>
17 #include <string.h>
18 #include <sys/stat.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <getopt.h>
23 #include <dvdread/ifo_read.h>
24 #include "lsdvd.h"
25 #include "ocode.h"
26
27 static struct { char code[3]; char name[20];}
28 language[] = {
29 { " ", "Not Specified" }, { "aa", "Afar" }, { "ab", "Abkhazian" }, { "af", "Afrikaans" }, { "am", "Amharic" },
30 { "ar", "Arabic" }, { "as", "Assamese" }, { "ay", "Aymara" }, { "az", "Azerbaijani" }, { "ba", "Bashkir" },
31 { "be", "Byelorussian" }, { "bg", "Bulgarian" }, { "bh", "Bihari" }, { "bi", "Bislama" }, { "bn", "Bengali; Bangla" },
32 { "bo", "Tibetan" }, { "br", "Breton" }, { "ca", "Catalan" }, { "co", "Corsican" }, { "cs", "Czech" },
33 { "cy", "Welsh" }, { "da", "Dansk" }, { "de", "Deutsch" }, { "dz", "Bhutani" }, { "el", "Greek" }, { "en", "English" },
34 { "eo", "Esperanto" }, { "es", "Espanol" }, { "et", "Estonian" }, { "eu", "Basque" }, { "fa", "Persian" },
35 { "fi", "Suomi" }, { "fj", "Fiji" }, { "fo", "Faroese" }, { "fr", "Francais" }, { "fy", "Frisian" }, { "ga", "Gaelic" },
36 { "gd", "Scots Gaelic" }, { "gl", "Galician" }, { "gn", "Guarani" }, { "gu", "Gujarati" }, { "ha", "Hausa" },
37 { "he", "Hebrew" }, { "hi", "Hindi" }, { "hr", "Hrvatski" }, { "hu", "Magyar" }, { "hy", "Armenian" },
38 { "ia", "Interlingua" }, { "id", "Indonesian" }, { "ie", "Interlingue" }, { "ik", "Inupiak" }, { "in", "Indonesian" },
39 { "is", "Islenska" }, { "it", "Italiano" }, { "iu", "Inuktitut" }, { "iw", "Hebrew" }, { "ja", "Japanese" },
40 { "ji", "Yiddish" }, { "jw", "Javanese" }, { "ka", "Georgian" }, { "kk", "Kazakh" }, { "kl", "Greenlandic" },
41 { "km", "Cambodian" }, { "kn", "Kannada" }, { "ko", "Korean" }, { "ks", "Kashmiri" }, { "ku", "Kurdish" },
42 { "ky", "Kirghiz" }, { "la", "Latin" }, { "ln", "Lingala" }, { "lo", "Laothian" }, { "lt", "Lithuanian" },
43 { "lv", "Latvian, Lettish" }, { "mg", "Malagasy" }, { "mi", "Maori" }, { "mk", "Macedonian" }, { "ml", "Malayalam" },
44 { "mn", "Mongolian" }, { "mo", "Moldavian" }, { "mr", "Marathi" }, { "ms", "Malay" }, { "mt", "Maltese" },
45 { "my", "Burmese" }, { "na", "Nauru" }, { "ne", "Nepali" }, { "nl", "Nederlands" }, { "no", "Norsk" }, { "oc", "Occitan" },
46 { "om", "Oromo" }, { "or", "Oriya" }, { "pa", "Punjabi" }, { "pl", "Polish" }, { "ps", "Pashto, Pushto" },
47 { "pt", "Portugues" }, { "qu", "Quechua" }, { "rm", "Rhaeto-Romance" }, { "rn", "Kirundi" }, { "ro", "Romanian" },
48 { "ru", "Russian" }, { "rw", "Kinyarwanda" }, { "sa", "Sanskrit" }, { "sd", "Sindhi" }, { "sg", "Sangho" },
49 { "sh", "Serbo-Croatian" }, { "si", "Sinhalese" }, { "sk", "Slovak" }, { "sl", "Slovenian" }, { "sm", "Samoan" },
50 { "sn", "Shona" }, { "so", "Somali" }, { "sq", "Albanian" }, { "sr", "Serbian" }, { "ss", "Siswati" },
51 { "st", "Sesotho" }, { "su", "Sundanese" }, { "sv", "Svenska" }, { "sw", "Swahili" }, { "ta", "Tamil" },
52 { "te", "Telugu" }, { "tg", "Tajik" }, { "th", "Thai" }, { "ti", "Tigrinya" }, { "tk", "Turkmen" }, { "tl", "Tagalog" },
53 { "tn", "Setswana" }, { "to", "Tonga" }, { "tr", "Turkish" }, { "ts", "Tsonga" }, { "tt", "Tatar" }, { "tw", "Twi" },
54 { "ug", "Uighur" }, { "uk", "Ukrainian" }, { "ur", "Urdu" }, { "uz", "Uzbek" }, { "vi", "Vietnamese" },
55 { "vo", "Volapuk" }, { "wo", "Wolof" }, { "xh", "Xhosa" }, { "yi", "Yiddish" }, { "yo", "Yoruba" }, { "za", "Zhuang" },
56 { "zh", "Chinese" }, { "zu", "Zulu" }, { "xx", "Unknown" }, { "\0", "Unknown" } };
57 char *video_format[2] = {"NTSC", "PAL"};
58 /* 28.9.2003: Chicken run's aspect ratio is 16:9 or 1.85:1, at index
59 1. Addionaly using ' in the quoting makes the perl output not
60 parse so changed to " */
61 char *aspect_ratio[4] = {"4/3", "16/9", "\"?:?\"", "16/9"};
62 char *quantization[4] = {"16bit", "20bit", "24bit", "drc"};
63 char *mpeg_version[2] = {"mpeg1", "mpeg2"};
64 /* 28.9.2003: The European chicken run title has height index 3, and
65 576 lines seems right, mplayer -vop cropdetect shows from 552 to
66 576 lines. What the correct value is for index 2 is harder to say */
67 char *video_height[4] = {"480", "576", "???", "576"};
68 char *video_width[4] = {"720", "704", "352", "352"};
69 char *permitted_df[4] = {"P&S + Letter", "Pan&Scan", "Letterbox", "?"};
70 char *audio_format[7] = {"ac3", "?", "mpeg1", "mpeg2", "lpcm ", "sdds ", "dts"};
71 int audio_id[7] = {0x80, 0, 0xC0, 0xC0, 0xA0, 0, 0x88};
72 /* 28.9.2003: Chicken run again, it has frequency index of 1.
73 According to dvd::rip the frequency is 48000 */
74 char *sample_freq[2] = {"48000", "48000"};
75 char *audio_type[5] = {"Undefined", "Normal", "Impaired", "Comments1", "Comments2"};
76 char *subp_type[16] = {"Undefined", "Normal", "Large", "Children", "reserved", "Normal_CC", "Large_CC", "Children_CC",
77 "reserved", "Forced", "reserved", "reserved", "reserved", "Director", "Large_Director", "Children_Director"};
78 double frames_per_s[4] = {-1.0, 25.00, -1.0, 29.97};
79
80 char* program_name;
81
82 #if 0
83 extern void operl_print(struct dvd_info *dvd_info);
84 extern void oxml_print(struct dvd_info *dvd_info);
85 extern void oruby_print(struct dvd_info *dvd_info);
86 extern void ohuman_print(struct dvd_info *dvd_info);
87 #endif
88
dvdtime2msec(dvd_time_t * dt)89 static int dvdtime2msec(dvd_time_t *dt)
90 {
91 double fps = frames_per_s[(dt->frame_u & 0xc0) >> 6];
92 long ms;
93 ms = (((dt->hour & 0xf0) >> 3) * 5 + (dt->hour & 0x0f)) * 3600000;
94 ms += (((dt->minute & 0xf0) >> 3) * 5 + (dt->minute & 0x0f)) * 60000;
95 ms += (((dt->second & 0xf0) >> 3) * 5 + (dt->second & 0x0f)) * 1000;
96
97 if(fps > 0)
98 ms += (((dt->frame_u & 0x30) >> 3) * 5 + (dt->frame_u & 0x0f)) * 1000.0 / fps;
99
100 return ms;
101 }
102
103 /*
104 * This is used to add up sets of times in the struct. it's not elegant at all
105 * but a quick way to easily add up 4 times at once. tracking the times in usec's
106 * constantly is easier, but without using math.h, it sucks to actually DO anything with it
107 * also it ***has*** to be better to return the playback_time, not just mess with it like this
108 */
converttime(playback_time_t * pt,dvd_time_t * dt)109 static void converttime(playback_time_t *pt, dvd_time_t *dt)
110 {
111 double fps = frames_per_s[(dt->frame_u & 0xc0) >> 6];
112
113 pt->usec = pt->usec + (((dt->frame_u & 0x30) >> 3) * 5 + (dt->frame_u & 0x0f)) * 1000.0 / fps;
114 pt->second = pt->second + ((dt->second & 0xf0) >> 3) * 5 + (dt->second & 0x0f);
115 pt->minute = pt->minute + ((dt->minute & 0xf0) >> 3) * 5 + (dt->minute & 0x0f);
116 pt->hour = pt->hour + ((dt->hour & 0xf0) >> 3) * 5 + (dt->hour & 0x0f);
117
118 if ( pt->usec >= 1000 ) { pt->usec -= 1000; pt->second++; }
119 if ( pt->second >= 60 ) { pt->second -= 60; pt->minute++; }
120 if ( pt->minute > 59 ) { pt->minute -= 60; pt->hour++; }
121 }
122
123 /*
124 * The following method is based on code from vobcopy, by Robos, with thanks.
125 */
get_title_name(const char * dvd_device,char * title)126 static int get_title_name(const char* dvd_device, char* title)
127 {
128 FILE *filehandle = 0;
129 int i;
130
131 if (! (filehandle = fopen(dvd_device, "r"))) {
132 fprintf(stderr, "Couldn't open %s for title\n", dvd_device);
133 strcpy(title, "unknown");
134 return -1;
135 }
136
137 if ( fseek(filehandle, 32768, SEEK_SET )) {
138 fclose(filehandle);
139 fprintf(stderr, "Couldn't seek in %s for title\n", dvd_device);
140 strcpy(title, "unknown");
141 return -1;
142 }
143
144 {
145 #define DVD_SEC_SIZ 2048
146 char tempBuf[ DVD_SEC_SIZ ];
147
148 if ( DVD_SEC_SIZ != fread(tempBuf, 1, DVD_SEC_SIZ, filehandle) ) {
149 fclose(filehandle);
150 fprintf(stderr, "Couldn't read enough bytes for title.\n");
151 strcpy(title, "unknown");
152 return -1;
153 }
154 snprintf( title, 32, "%s", tempBuf + 40 );
155 i=32;
156 }
157
158 fclose (filehandle);
159
160 title[32] = '\0';
161 while(i-- > 2)
162 if(title[i] == ' ') title[i] = '\0';
163 return 0;
164 }
165
lang_name(char * code)166 static char* lang_name(char* code)
167 {
168 int k=0;
169 while (memcmp(language[k].code, code, 2) && language[k].name[0] ) { k++; }
170 return language[k].name;
171 }
172
version(void)173 static void version(void)
174 {
175 fprintf(stderr, "lsdvd "VERSION" - GPL Copyright (c) 2002-2005, 2014 \"Written\" by Chris Phillips <acid_kewpie@users.sf.net>\n");
176 }
177
usage(void)178 static void usage(void)
179 {
180 version();
181 fprintf(stderr, "Usage: %s [options] [-t track_number] [dvd path] \n", program_name);
182 fprintf(stderr, "\n");
183 fprintf(stderr, "Options:\n");
184 fprintf(stderr, "\tExtra information:\n");
185 fprintf(stderr, "\t -a audio streams\n");
186 fprintf(stderr, "\t -d cells\n");
187 fprintf(stderr, "\t -n angles\n");
188 fprintf(stderr, "\t -c chapters\n");
189 fprintf(stderr, "\t -s subpictures\n");
190 fprintf(stderr, "\t -P palette\n");
191 fprintf(stderr, "\t -v video\n");
192 fprintf(stderr, "\t -x all information\n");
193 fprintf(stderr, "\n");
194 fprintf(stderr, "\tFormatting:\n");
195 fprintf(stderr, "\t -Oh output as human readable (default)\n");
196 fprintf(stderr, "\t -Op output as Perl\n");
197 fprintf(stderr, "\t -Oy output as Python\n");
198 fprintf(stderr, "\t -Or output as Ruby\n");
199 fprintf(stderr, "\t -Ox output as XML\n");
200 /* fprintf(stderr, "\t -p output as Perl [deprecated]\n"); */
201 fprintf(stderr, "\n");
202 fprintf(stderr, "\tOther options:\n");
203 fprintf(stderr, "\t -q quiet - no summary totals\n");
204 fprintf(stderr, "\t -h this message\n");
205 fprintf(stderr, "\t -V version information\n");
206 fprintf(stderr, "\n");
207 }
208
209 int opt_a=0, opt_c=0, opt_n=0, opt_p=0, opt_q=0, opt_s=0, opt_t=0, opt_v=0, opt_x=0, opt_d=0, opt_P=0;
210 char opt_O='h';
211
output_option(char * arg)212 static char output_option(char *arg)
213 {
214 if (strlen(arg) == 1) {
215 return arg[0];
216 } else if (strcmp(arg, "perl") == 0) {
217 return 'p';
218 } else if (strcmp(arg, "python") == 0) {
219 return 'y';
220 } else if (strcmp(arg, "ruby") == 0) {
221 return 'r';
222 } else if (strcmp(arg, "ruby2") == 0) {
223 return '2';
224 } else if (strcmp(arg, "xml") == 0) {
225 return 'x';
226 } else if (strcmp(arg, "human") == 0) {
227 return 'h';
228 } else {
229 return '\0';
230 }
231 }
232
233
main(int argc,char * argv[])234 int main(int argc, char *argv[])
235 {
236 char title[33];
237 dvd_reader_t *dvd;
238 ifo_handle_t *ifo_zero, **ifo;
239 pgcit_t *vts_pgcit;
240 vtsi_mat_t *vtsi_mat;
241 vmgi_mat_t *vmgi_mat;
242 audio_attr_t *audio_attr;
243 video_attr_t *video_attr;
244 subp_attr_t *subp_attr;
245 pgc_t *pgc;
246 int i, j, k, c, titles, cell, vts_ttn, title_set_nr;
247 char lang_code[3];
248 char *dvd_device = "/dev/cd0";
249 int has_title = 0, ret = 0;
250 int max_length = 0, max_track = 0;
251 struct stat dvd_stat;
252
253 program_name = argv[0];
254
255 while ((c = getopt(argc, argv, "acnpPqsdvt:O:xhV?")) != EOF) {
256 switch (c) {
257 case 'h':
258 case '?': usage(); return 0;
259 case 'V': version(); return 0;
260 case 'a': opt_a = 1; break;
261 case 'd': opt_d = 1; break;
262 case 's': opt_s = 1; break;
263 case 'q': opt_q = 1; break;
264 case 'c': opt_c = 1; break;
265 case 'n': opt_n = 1; break;
266 case 'p': opt_p = 1; break;
267 case 'P': opt_P = 1; break;
268 case 't': opt_t = atoi(optarg); break;
269 case 'O': opt_O = output_option(optarg); break;
270 case 'v': opt_v = 1; break;
271 case 'x': opt_x = 1;
272 opt_a = 1;
273 opt_c = 1;
274 opt_s = 1;
275 opt_P = 1;
276 opt_d = 1;
277 opt_n = 1;
278 opt_v = 1; break;
279 }
280 }
281
282 if (argv[optind]) { dvd_device = argv[optind]; }
283
284 ret = stat(dvd_device, &dvd_stat);
285 if ( ret < 0 ) {
286 fprintf(stderr, "Can't find device %s\n", dvd_device);
287 return 1;
288 }
289
290 dvd = DVDOpen(dvd_device);
291 if( !dvd ) {
292 fprintf( stderr, "Can't open disc %s!\n", dvd_device);
293 return 2;
294 }
295 ifo_zero = ifoOpen(dvd, 0);
296 if ( !ifo_zero ) {
297 fprintf( stderr, "Can't open main ifo!\n");
298 return 3;
299 }
300
301 ifo = (ifo_handle_t **)malloc((ifo_zero->vts_atrt->nr_of_vtss + 1) * sizeof(ifo_handle_t *));
302
303 for (i=1; i <= ifo_zero->vts_atrt->nr_of_vtss; i++) {
304 ifo[i] = ifoOpen(dvd, i);
305 if ( !ifo[i] && opt_t == i ) {
306 fprintf( stderr, "Can't open ifo %d!\n", i);
307 return 4;
308 }
309 }
310
311 titles = ifo_zero->tt_srpt->nr_of_srpts;
312
313 if ( opt_t > titles || opt_t < 0) {
314 fprintf (stderr, "Only %d titles on this disc!", titles);
315 return 5;
316 }
317
318 has_title = get_title_name(dvd_device, title);
319
320 vmgi_mat = ifo_zero->vmgi_mat;
321
322 struct dvd_info dvd_info;
323
324 dvd_info.discinfo.device = dvd_device;
325 dvd_info.discinfo.disc_title = has_title ? "unknown" : title;
326 dvd_info.discinfo.vmg_id = vmgi_mat->vmg_identifier;
327 dvd_info.discinfo.provider_id = vmgi_mat->provider_identifier;
328
329 dvd_info.title_count = titles;
330 dvd_info.titles = calloc(titles, sizeof(*dvd_info.titles));
331
332 for (j=0; j < titles; j++) {
333
334 if ( opt_t == j+1 || opt_t == 0 ) {
335
336 /* GENERAL */
337 if (ifo[ifo_zero->tt_srpt->title[j].title_set_nr]->vtsi_mat) {
338
339 dvd_info.titles[j].enabled = 1;
340
341 vtsi_mat = ifo[ifo_zero->tt_srpt->title[j].title_set_nr]->vtsi_mat;
342 vts_pgcit = ifo[ifo_zero->tt_srpt->title[j].title_set_nr]->vts_pgcit;
343 video_attr = &vtsi_mat->vts_video_attr;
344 vts_ttn = ifo_zero->tt_srpt->title[j].vts_ttn;
345 vmgi_mat = ifo_zero->vmgi_mat;
346 title_set_nr = ifo_zero->tt_srpt->title[j].title_set_nr;
347 pgc = vts_pgcit->pgci_srp[ifo[title_set_nr]->vts_ptt_srpt->title[vts_ttn - 1].ptt[0].pgcn - 1].pgc;
348 dvd_info.titles[j].general.vts_id = vtsi_mat->vts_identifier;
349 dvd_info.titles[j].chapter_count_reported = ifo_zero->tt_srpt->title[j].nr_of_ptts;
350 if(pgc->cell_playback == NULL || pgc->program_map == NULL) {
351 dvd_info.titles[j].general.length = 0;
352 dvd_info.titles[j].general.playback_time.hour = 0;
353 dvd_info.titles[j].general.playback_time.minute = 0;
354 dvd_info.titles[j].general.playback_time.second = 0;
355 dvd_info.titles[j].general.playback_time.usec = 0;
356 dvd_info.titles[j].chapter_count = 0;
357 dvd_info.titles[j].cell_count = 0;
358 dvd_info.titles[j].audiostream_count_reported = 0;
359 dvd_info.titles[j].audiostream_count = 0;
360 dvd_info.titles[j].subtitle_count_reported = 0;
361 dvd_info.titles[j].subtitle_count = 0;
362 } else {
363 dvd_info.titles[j].general.length = dvdtime2msec(&pgc->playback_time)/1000.0;
364 converttime(&dvd_info.titles[j].general.playback_time, &pgc->playback_time);
365 dvd_info.titles[j].chapter_count = pgc->nr_of_programs;
366 dvd_info.titles[j].cell_count = pgc->nr_of_cells;
367 dvd_info.titles[j].audiostream_count_reported = vtsi_mat->nr_of_vts_audio_streams;
368 dvd_info.titles[j].audiostream_count = 0;
369 for(k = 0; k < dvd_info.titles[j].audiostream_count_reported; k++) {
370 if((pgc->audio_control[k] & 0x8000) != 0) {
371 dvd_info.titles[j].audiostream_count++;
372 }
373 }
374 dvd_info.titles[j].subtitle_count_reported = vtsi_mat->nr_of_vts_subp_streams;
375 dvd_info.titles[j].subtitle_count = 0;
376 for(k = 0; k < dvd_info.titles[j].subtitle_count_reported; k++) {
377 if ((pgc->subp_control[k] & 0x80000000) != 0) {
378 dvd_info.titles[j].subtitle_count++;
379 }
380 }
381 if (dvdtime2msec(&pgc->playback_time) > max_length) {
382 max_length = dvdtime2msec(&pgc->playback_time);
383 max_track = j+1;
384 }
385 }
386
387 if(opt_v) {
388 dvd_info.titles[j].parameter.vts = ifo_zero->tt_srpt->title[j].title_set_nr;
389 dvd_info.titles[j].parameter.ttn = ifo_zero->tt_srpt->title[j].vts_ttn;
390 dvd_info.titles[j].parameter.fps = frames_per_s[(pgc->playback_time.frame_u & 0xc0) >> 6];
391 dvd_info.titles[j].parameter.format = video_format[video_attr->video_format];
392 dvd_info.titles[j].parameter.aspect = aspect_ratio[video_attr->display_aspect_ratio];
393 dvd_info.titles[j].parameter.width = video_width[video_attr->picture_size];
394 dvd_info.titles[j].parameter.height = video_height[video_attr->video_format];
395 dvd_info.titles[j].parameter.df = permitted_df[video_attr->permitted_df];
396
397 }
398
399 /* PALETTE */
400 if (opt_P) {
401 const int palsize = 16;
402 dvd_info.titles[j].palette = malloc(palsize * sizeof(int));
403 for (i=0; i < palsize; i++) { dvd_info.titles[j].palette[i] = pgc->palette[i]; }
404 } else {
405 dvd_info.titles[j].palette = NULL;
406 }
407
408 /* ANGLES */
409
410 if (opt_n) {
411 dvd_info.titles[j].angle_count = ifo_zero->tt_srpt->title[j].nr_of_angles;
412 } else {
413 dvd_info.titles[j].angle_count = 0;
414 }
415
416 /* AUDIO */
417
418 if (opt_a) {
419
420 dvd_info.titles[j].audiostreams = calloc(dvd_info.titles[j].audiostream_count, sizeof(*dvd_info.titles[j].audiostreams));
421
422 for (i=0, k=0; i<dvd_info.titles[j].audiostream_count_reported; i++)
423 {
424 if((pgc->audio_control[i] & 0x8000) == 0) {
425 continue;
426 }
427 audio_attr = &vtsi_mat->vts_audio_attr[i];
428 sprintf(lang_code, "%c%c", audio_attr->lang_code>>8, audio_attr->lang_code & 0xff);
429 if (!lang_code[0]) { lang_code[0] = 'x'; lang_code[1] = 'x'; }
430
431 dvd_info.titles[j].audiostreams[k].langcode = strdup(lang_code);
432 dvd_info.titles[j].audiostreams[k].language = lang_name(lang_code);
433 dvd_info.titles[j].audiostreams[k].format = audio_format[audio_attr->audio_format];
434 dvd_info.titles[j].audiostreams[k].frequency = sample_freq[audio_attr->sample_frequency];
435 dvd_info.titles[j].audiostreams[k].quantization = quantization[audio_attr->quantization];
436 dvd_info.titles[j].audiostreams[k].channels = audio_attr->channels+1;
437 dvd_info.titles[j].audiostreams[k].ap_mode = audio_attr->application_mode;
438 dvd_info.titles[j].audiostreams[k].content = audio_type[audio_attr->code_extension];
439 dvd_info.titles[j].audiostreams[k].streamid = audio_id[audio_attr->audio_format] + i;
440 k++;
441 }
442 } else {
443 dvd_info.titles[j].audiostreams = NULL;
444 }
445
446 /* CHAPTERS */
447
448 cell = 0;
449 if (opt_c) {
450 dvd_info.titles[j].chapters = calloc(dvd_info.titles[j].chapter_count, sizeof(*dvd_info.titles[j].chapters));
451
452 int ms;
453 for (i=0; i<dvd_info.titles[j].chapter_count; i++)
454 {
455 ms=0;
456 int next = pgc->program_map[i+1];
457 if (i == pgc->nr_of_programs - 1) next = pgc->nr_of_cells + 1;
458
459 while (cell < next - 1)
460 {
461 ms = ms + dvdtime2msec(&pgc->cell_playback[cell].playback_time);
462 converttime(&dvd_info.titles[j].chapters[i].playback_time, &pgc->cell_playback[cell].playback_time);
463 cell++;
464 }
465 dvd_info.titles[j].chapters[i].startcell = pgc->program_map[i];
466 dvd_info.titles[j].chapters[i].length = ms * 0.001;
467
468
469 }
470 }
471
472 /* CELLS */
473
474
475 dvd_info.titles[j].cells = calloc(dvd_info.titles[j].cell_count, sizeof(*dvd_info.titles[j].cells));
476
477 if (opt_d) {
478 for (i=0; i<dvd_info.titles[j].cell_count; i++)
479 {
480 dvd_info.titles[j].cells[i].length = dvdtime2msec(&pgc->cell_playback[i].playback_time)/1000.0;
481 converttime(&dvd_info.titles[j].cells[i].playback_time, &pgc->cell_playback[i].playback_time);
482 /* added to get the start/end sectors */
483 dvd_info.titles[j].cells[i].first_sector = pgc->cell_playback[i].first_sector;
484 dvd_info.titles[j].cells[i].last_sector = pgc->cell_playback[i].last_sector;
485
486 }
487 } else {
488 dvd_info.titles[j].cells = NULL;
489 }
490
491 /* SUBTITLES */
492
493 dvd_info.titles[j].subtitles = calloc(dvd_info.titles[j].subtitle_count, sizeof(*dvd_info.titles[j].subtitles));
494
495 if (opt_s) {
496 for (i=0, k=0; i<dvd_info.titles[j].subtitle_count; i++)
497 {
498 if ((pgc->subp_control[k] & 0x80000000) == 0)
499 continue;
500 subp_attr = &vtsi_mat->vts_subp_attr[i];
501 sprintf(lang_code, "%c%c", subp_attr->lang_code>>8, subp_attr->lang_code & 0xff);
502 if (!lang_code[0]) { lang_code[0] = 'x'; lang_code[1] = 'x'; }
503
504 dvd_info.titles[j].subtitles[k].langcode = strdup(lang_code);
505 dvd_info.titles[j].subtitles[k].language = lang_name(lang_code);
506 dvd_info.titles[j].subtitles[k].content = subp_type[subp_attr->code_extension];
507 dvd_info.titles[j].subtitles[k].streamid = 0x20 + i;
508 k++;
509 }
510 } else {
511 dvd_info.titles[j].subtitles = NULL;
512 }
513
514 } /* if vtsi_mat */
515 } /* if not -t */
516 } /* for each title */
517
518 if (! opt_t) { dvd_info.longest_track = max_track; }
519
520 if (opt_p) {
521 ocode_print(&perl_syntax, &dvd_info);
522 } else {
523 switch(opt_O) {
524 case 'p':
525 ocode_print(&perl_syntax, &dvd_info);
526 break;
527 case 'y':
528 ocode_print(&python_syntax, &dvd_info);
529 break;
530 case 'x':
531 oxml_print(&dvd_info);
532 break;
533 case 'r':
534 ocode_print(&ruby_syntax, &dvd_info);
535 break;
536 case 'd':
537 ocode_print(&debug_syntax, &dvd_info);
538 break;
539 default :
540 ohuman_print(&dvd_info);
541 break;
542 }
543 }
544
545 for (i=1; i <= ifo_zero->vts_atrt->nr_of_vtss; i++) { ifoClose(ifo[i]); }
546
547 ifoClose(ifo_zero);
548 DVDClose(dvd);
549
550 return 0;
551 }
552