1 /*
2 Higher-level definitions for building DVD authoring structures
3 */
4 /*
5 * Copyright (C) 2002 Scott Smith (trckjunky@users.sourceforge.net)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301 USA.
21 */
22
23 #include "compat.h"
24
25 #include <sys/types.h>
26 #include <dirent.h>
27 #include <assert.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <dirent.h>
32
33 #include "dvdauthor.h"
34 #include "da-internal.h"
35 #include "dvdvm.h"
36
37
38
39 // with this enabled, extra PGC commands will be generated to allow
40 // jumping/calling to a wider number of destinations
41 bool jumppad = false;
42
43 // with this enabled, all 16 general purpose registers can be used, but
44 // prohibits certain convenience features, like multiple commands on a button
45 bool allowallreg = false;
46
47 /* video/audio/subpicture attribute keywords -- note they are all unique to allow
48 xxx_ANY attribute setting to work */
49 static const char * const vmpegdesc[4]={"","mpeg1","mpeg2",0};
50 static const char * const vresdesc[6]={"","720xfull","704xfull","352xfull","352xhalf",0};
51 static const char * const vformatdesc[4]={"","ntsc","pal",0};
52 static const char * const vaspectdesc[4]={"","4:3","16:9",0};
53 static const char * const vwidescreendesc[5]={"","noletterbox","nopanscan","crop",0};
54 // taken from mjpegtools, also GPL
55 const static char * const vratedesc[16] = /* descriptions of frame-rate codes */
56 {
57 "0x0",
58 "24000.0/1001.0 (NTSC 3:2 pulldown converted FILM)",
59 "24.0 (NATIVE FILM)",
60 "25.0 (PAL/SECAM VIDEO / converted FILM)",
61 "30000.0/1001.0 (NTSC VIDEO)",
62 "30.0",
63 "50.0 (PAL FIELD RATE)",
64 "60000.0/1001.0 (NTSC FIELD RATE)",
65 "60.0",
66 /* additional rates copied from FFmpeg, really just to fill out array */
67 "15.0",
68 "5.0",
69 "10.0",
70 "12.0",
71 "15.0",
72 "0xe",
73 "0xf"
74 };
75 static const char * const aformatdesc[6]={"","ac3","mp2","pcm","dts",0};
76 /* audio formats */
77 static const char * const aquantdesc[6]={"","16bps","20bps","24bps","drc",0};
78 static const char * const adolbydesc[3]={"","surround",0};
79 static const char * const alangdesc[4]={"","nolang","lang",0};
80 static const char * const achanneldesc[10]={"","1ch","2ch","3ch","4ch","5ch","6ch","7ch","8ch",0};
81 static const char * const asampledesc[4]={"","48khz","96khz",0};
82 /* audio sample rates */
83 static const char * const acontentdesc[6] =
84 {"", "normal", "impaired", "comments1", "comments2", 0};
85 /* audio content types */
86
87 const char * const entries[9]={"","","title","root","subtitle","audio","angle","ptt",0};
88 /* entry menu types */
89
90 const char * const pstypes[3]={"VTS","VTSM","VMGM"};
91
92 static const char * const smodedesc[6]={"","normal","widescreen","letterbox","panscan",0};
93 /* subpicture usage modes */
94 static const char * const scontentdesc[17] =
95 {
96 "", "normal", "large", "children",
97 "", "normal_cc", "large_cc", "children_cc",
98 "", "forced", "", "",
99 "", "director", "large_director", "children_director",
100 0
101 };
102 /* subpicture content types */
103
104 static const int default_colors[16]={ /* default contents for new colour tables */
105 COLOR_UNUSED,
106 COLOR_UNUSED,
107 COLOR_UNUSED,
108 COLOR_UNUSED,
109
110 COLOR_UNUSED,
111 COLOR_UNUSED,
112 COLOR_UNUSED,
113 COLOR_UNUSED,
114
115 COLOR_UNUSED,
116 COLOR_UNUSED,
117 COLOR_UNUSED,
118 COLOR_UNUSED,
119
120 COLOR_UNUSED,
121 COLOR_UNUSED,
122 COLOR_UNUSED,
123 COLOR_UNUSED
124 };
125
126 static const int ratedenom[9]={0,90090,90000,90000,90090,90000,90000,90090,90000};
127 /* corresponding to vratedesc, adjustment to clock units per second
128 to convert nominal to actual frame rate */
129 static const int evenrate[9]={0, 24, 24, 25, 30, 30, 50, 60, 60};
130 /* corresponding to vratedesc, nominal frame rate */
131
132 bool delete_output_dir = false;
133
getratecode(const struct vobgroup * va)134 static int getratecode(const struct vobgroup *va)
135 /* returns the frame rate code if specified, else the default. */
136 {
137 if (va->vd.vframerate != 0)
138 return va->vd.vframerate;
139 else if (va->vd.vformat != VF_NONE || default_video_format != VF_NONE)
140 {
141 /* fudge it for calls from menu PGC-generation routines with no video present */
142 return (va->vd.vformat != VF_NONE ? va->vd.vformat : default_video_format) == VF_PAL ? VR_PAL : VR_NTSC;
143 }
144 else
145 {
146 #if defined(DEFAULT_VIDEO_FORMAT)
147 # if DEFAULT_VIDEO_FORMAT == 1
148 fprintf(stderr, "WARN: defaulting frame rate to NTSC\n");
149 return VR_NTSC;
150 # elif DEFAULT_VIDEO_FORMAT == 2
151 fprintf(stderr, "WARN: defaulting frame rate to PAL\n");
152 return VR_PAL;
153 # endif
154 #else
155 fprintf(stderr, "ERR: cannot determine default frame rate--no video format specified\n");
156 exit(1);
157 #endif
158 } /*if*/
159 } /*getratecode*/
160
getratedenom(const struct vobgroup * va)161 int getratedenom(const struct vobgroup *va)
162 /* returns the frame rate divider for the frame rate if specified, else the default. */
163 {
164 return ratedenom[getratecode(va)];
165 } /*getratedenom*/
166
getframepts(const struct vobgroup * va)167 pts_t getframepts(const struct vobgroup *va)
168 /* returns the number of exact clock units per frame. */
169 {
170 const int rc = getratecode(va);
171 return ratedenom[rc] / evenrate[rc];
172 } /*getframepts*/
173
tobcd(int v)174 static int tobcd(int v)
175 /* separates the two decimal digits of v (assumed in range [0 .. 99]) into two hex nibbles.
176 This is used for encoding cell and PGC playback times. */
177 {
178 return (v / 10) * 16 + v % 10;
179 } /*tobcd*/
180
buildtimehelper(const struct vobgroup * va,int64_t num,int64_t denom)181 static unsigned int buildtimehelper(const struct vobgroup *va, int64_t num, int64_t denom)
182 /* returns a BCD-encoded representation hhmmssff of num/denom seconds including
183 the frame rate. */
184 {
185 int hr, min, sec, fr, rc;
186 int64_t frate;
187
188 if (denom == 90090)
189 {
190 frate = 30;
191 rc = 3;
192 }
193 else
194 {
195 frate = 25;
196 rc = 1;
197 } /*if*/
198 num += denom / (frate * 2) + 1; /* so duration will be rounded to nearest whole frame time */
199 sec = num / denom; /* seconds */
200 min = sec / 60;
201 hr = tobcd(min / 60); /* hours */
202 min = tobcd(min % 60); /* minutes */
203 sec = tobcd(sec % 60); /* seconds */
204 num %= denom;
205 fr = tobcd(num * frate / denom); /* frame number within second--note tens digit will be <= 3 */
206 return
207 hr << 24
208 |
209 min << 16
210 |
211 sec << 8
212 |
213 fr
214 |
215 rc << 6;
216 } /*buildtimehelper*/
217
buildtimeeven(const struct vobgroup * va,int64_t num)218 unsigned int buildtimeeven(const struct vobgroup *va, int64_t num)
219 /* returns a BCD-encoded representation hhmmssff of num/denom seconds, where
220 denom is computed according to va->vd.vframerate. This is used for encoding
221 cell and PGC playback times. I think these BCD-encoded fields are designed
222 to be easy for the player to convert to a a form that can be displayed to
223 the user, they're not going to be used for any other computations in the
224 player. */
225 {
226 const int rc = getratecode(va);
227 return
228 buildtimehelper(va, num, ratedenom[rc]);
229 } /*buildtimeeven*/
230
getaudch(const struct vobgroup * va,int a)231 int getaudch(const struct vobgroup *va, int a)
232 /* returns an index into a vob.audch array, with the audio format in the top two bits
233 and the channel id in the bottom three bits. */
234 {
235 if (!va->ad[a].aid)
236 return
237 -1;
238 return
239 va->ad[a].aid - 1 + (va->ad[a].aformat - 1) * 8;
240 } /*getaudch*/
241
write8(unsigned char * p,unsigned char d0,unsigned char d1,unsigned char d2,unsigned char d3,unsigned char d4,unsigned char d5,unsigned char d6,unsigned char d7)242 void write8(unsigned char *p,unsigned char d0,unsigned char d1,unsigned char d2,unsigned char d3,unsigned char d4,unsigned char d5,unsigned char d6,unsigned char d7)
243 /* stores 8 bytes beginning at address p. */
244 {
245 p[0]=d0;
246 p[1]=d1;
247 p[2]=d2;
248 p[3]=d3;
249 p[4]=d4;
250 p[5]=d5;
251 p[6]=d6;
252 p[7]=d7;
253 }
254
write4(unsigned char * p,unsigned int v)255 void write4(unsigned char *p,unsigned int v)
256 /* inserts a four-byte integer in big-endian format beginning at address p. */
257 {
258 p[0]=(v>>24)&255;
259 p[1]=(v>>16)&255;
260 p[2]=(v>>8)&255;
261 p[3]=v&255;
262 }
263
write2(unsigned char * p,unsigned int v)264 void write2(unsigned char *p,unsigned int v)
265 /* inserts a two-byte integer in big-endian format beginning at address p. */
266 {
267 p[0]=(v>>8)&255;
268 p[1]=v&255;
269 }
270
read4(const unsigned char * p)271 unsigned int read4(const unsigned char *p)
272 /* extracts a four-byte integer in big-endian format beginning at address p. */
273 {
274 return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
275 }
276
read2(const unsigned char * p)277 unsigned int read2(const unsigned char *p)
278 /* extracts a two-byte integer in big-endian format beginning at address p. */
279 {
280 return (p[0]<<8)|p[1];
281 }
282
findsubpmode(const char * m)283 static int findsubpmode(const char *m)
284 /* returns the code corresponding to the specified subpicture usage mode name, or
285 -1 if unrecognized. */
286 {
287 int i;
288 for (i = 0; i < 4; i++)
289 if (!strcasecmp(smodedesc[i+1], m))
290 return
291 i;
292 return
293 -1;
294 } /*findsubpmode*/
295
warnupdate(int * oldval,int newval,int * warnval,const char * desc,const char * const * lookup)296 static int warnupdate
297 (
298 int * oldval,
299 int newval,
300 int * warnval, /* value previously warned about, if any */
301 const char * desc, /* explanatory text for warning message */
302 const char * const * lookup /* for converting values to symbolic form for messages */
303 )
304 /* updates *oldval to newval if not yet set (i.e. = 0), does nothing if it is already newval,
305 otherwise if it was set to something else, outputs a warning (if *warnval is not already
306 newval) and sets *warnval to newval. Returns 1 iff such a mismatch was found, else 0. */
307 {
308 if (oldval[0] == 0)
309 {
310 oldval[0] = newval;
311 return
312 0;
313 }
314 else if (oldval[0] == newval)
315 return
316 0;
317 else if (warnval[0] != newval) /* not already warned about this value */
318 {
319 fprintf
320 (
321 stderr,
322 "WARN: attempt to update %s from %s to %s; skipping\n",
323 desc,
324 lookup[oldval[0]],
325 lookup[newval]
326 );
327 warnval[0] = newval; /* to reduce number of warnings */
328 } /*if*/
329 return
330 1;
331 } /*warnupdate*/
332
scanandwarnupdate(int * oldval,const char * newval,int * warnval,const char * desc,const char * const * lookup)333 static int scanandwarnupdate
334 (
335 int * oldval,
336 const char * newval, /* symbolic new value */
337 int * warnval,
338 const char * desc, /* explanatory text for warning message */
339 const char * const * lookup /* table from which to return index matching newval */
340 )
341 /* updates *oldval to the index of the entry matching the name newval in the array lookup if
342 found and *oldval was not yet set (i.e. = 0). Does nothing if it was already set to the value,
343 otherwise if it was set to something else, outputs a warning and sets *warnval to the
344 new value. Returns 0 if newval could not be recognized, 1 if *oldval was updated or was
345 already the right value, and 2 if the warning was output. */
346 {
347 int i;
348 for (i = 1; lookup[i]; i++)
349 if (!strcasecmp(newval, lookup[i]))
350 return
351 warnupdate(oldval, i, warnval, desc, lookup) + 1;
352 return
353 0;
354 } /*scanandwarnupdate*/
355
vobgroup_set_video_framerate(struct vobgroup * va,int rate)356 int vobgroup_set_video_framerate(struct vobgroup *va, int rate /* [0 .. 15] */)
357 /* sets the video frame rate code (should be VR_PAL or VR_NTSC only). Returns 1 if
358 the framerate was already set to something different, else 0. */
359 {
360 if (!va->vd.vframerate && rate != VR_PAL && rate != VR_NTSC)
361 {
362 fprintf(stderr, "WARN: not a valid DVD frame rate: 0x%02x\n", rate);
363 rate = VR_NTSC; /* or something */
364 } /*if*/
365 return warnupdate(&va->vd.vframerate, rate, &va->vdwarn.vframerate, "frame rate", vratedesc);
366 } /*vobgroup_set_video_framerate*/
367
368 #define ATTRMATCH(a) (attr==0 || attr==(a))
369 /* does the attribute code match either the specified value or the xxx_ANY value */
370
vobgroup_set_video_attr(struct vobgroup * va,int attr,const char * s)371 int vobgroup_set_video_attr(struct vobgroup *va,int attr,const char *s)
372 /* sets the specified video attribute (might be VIDEO_ANY) to the specified keyword value.
373 Returns 1 if the attribute was already set to a different value, else 0.
374 Aborts the program on an unrecognizable value. */
375 {
376 int w;
377
378 if( ATTRMATCH(VIDEO_MPEG) ) {
379 w=scanandwarnupdate(&va->vd.vmpeg,s,&va->vdwarn.vmpeg,"mpeg format",vmpegdesc);
380 if(w) return w-1;
381 }
382
383 if( ATTRMATCH(VIDEO_FORMAT) ) {
384 w=scanandwarnupdate(&va->vd.vformat,s,&va->vdwarn.vformat,"tv format",vformatdesc);
385 if(w) return w-1;
386 }
387
388 if( ATTRMATCH(VIDEO_ASPECT) ) {
389 w=scanandwarnupdate(&va->vd.vaspect,s,&va->vdwarn.vaspect,"aspect ratio",vaspectdesc);
390 if(w) return w-1;
391 }
392
393 if( ATTRMATCH(VIDEO_WIDESCREEN) ) {
394 w=scanandwarnupdate(&va->vd.vwidescreen,s,&va->vdwarn.vwidescreen,"widescreen conversion",vwidescreendesc);
395 if(w) return w-1;
396 }
397
398 if (ATTRMATCH(VIDEO_CAPTION))
399 {
400 w = va->vd.vcaption;
401 if (!strcasecmp(s, "field1"))
402 va->vd.vcaption |= 1;
403 else if (!strcasecmp(s, "field2"))
404 va->vd.vcaption |= 2;
405 else
406 {
407 fprintf(stderr, "ERR: Cannot parse video caption '%s'\n", s);
408 exit(1);
409 } /*if*/
410 return w;
411 } /*if*/
412
413 if (ATTRMATCH(VIDEO_RESOLUTION) && strstr(s, "x"))
414 {
415 const int splitpos = strstr(s, "x") - s;
416 const char * const s1 = strndup(s, splitpos);
417 const char * const s2 = s + splitpos + 1;
418 const int h = strtounsigned(s1, "horizontal resolution");
419 int v, r, w;
420
421 if (isdigit(s2[0]))
422 v = strtounsigned(s2, "vertical resolution");
423 else if (!strcasecmp(s2, "full") || !strcasecmp(s2, "high"))
424 v = 384;
425 else
426 v = 383;
427
428 if (h > 704)
429 r = VS_720H;
430 else if (h > 352)
431 r = VS_704H;
432 else if (v >= 384)
433 r = VS_352H;
434 else
435 r = VS_352L;
436 w = warnupdate(&va->vd.vres, r, &va->vdwarn.vres, "resolution", vresdesc);
437
438 if (va->vd.vformat == VF_NONE)
439 {
440 if (v % 5 == 0)
441 va->vd.vformat = VF_NTSC;
442 else if (v % 9 == 0)
443 va->vd.vformat = VF_PAL;
444 } /*if*/
445 free((char *)s1);
446 return w;
447 } /*if*/
448
449 fprintf(stderr,"ERR: Cannot parse video option '%s'\n",s);
450 exit(1);
451 } /*vobgroup_set_video_attr*/
452
audiodesc_set_audio_attr(struct audiodesc * ad,struct audiodesc * adwarn,int attr,const char * s)453 int audiodesc_set_audio_attr(struct audiodesc *ad,struct audiodesc *adwarn,int attr,const char *s)
454 /* sets the specified audio attribute (might be AUDIO_ANY) to the specified keyword value.
455 Returns 1 if the attribute was already set to a different value, else 0.
456 Aborts the program on an unrecognizable value. */
457 {
458 int w;
459
460 if (ATTRMATCH(AUDIO_FORMAT)) {
461 w=scanandwarnupdate(&ad->aformat,s,&adwarn->aformat,"audio format",aformatdesc);
462 if(w) return w-1;
463 }
464
465 if (ATTRMATCH(AUDIO_QUANT)) {
466 w=scanandwarnupdate(&ad->aquant,s,&adwarn->aquant,"audio quantization",aquantdesc);
467 if(w) return w-1;
468 }
469
470 if (ATTRMATCH(AUDIO_DOLBY)) {
471 w=scanandwarnupdate(&ad->adolby,s,&adwarn->adolby,"surround",adolbydesc);
472 if(w) return w-1;
473 }
474
475 if (ATTRMATCH(AUDIO_ANY)) {
476 w=scanandwarnupdate(&ad->alangpresent,s,&adwarn->alangpresent,"audio language",alangdesc);
477 if(w) return w-1;
478 }
479
480 if (ATTRMATCH(AUDIO_CHANNELS)) {
481 w=scanandwarnupdate(&ad->achannels,s,&adwarn->achannels,"number of channels",achanneldesc);
482 if(w) return w-1;
483 }
484
485 if (ATTRMATCH(AUDIO_SAMPLERATE)) {
486 w=scanandwarnupdate(&ad->asample,s,&adwarn->asample,"sampling rate",asampledesc);
487 if(w) return w-1;
488 }
489
490 if (ATTRMATCH(AUDIO_LANG) && 2==strlen(s)) {
491 w=warnupdate(&ad->alangpresent,AL_LANG,&adwarn->alangpresent,"audio language",alangdesc);
492 /* turn on lang */
493 if(ad->lang[0] || ad->lang[1])
494 w=1; /* language code already set */
495 ad->lang[0]=tolower(s[0]); /* note I don't actually validate the language code */
496 ad->lang[1]=tolower(s[1]);
497 return w;
498 }
499
500 if (ATTRMATCH(AUDIO_CONTENT))
501 {
502 w = scanandwarnupdate(&ad->acontent, s, &adwarn->acontent, "audio content type", acontentdesc);
503 if(w)
504 return w - 1;
505 } /*if*/
506
507 fprintf(stderr,"ERR: Cannot parse audio option '%s'\n",s);
508 exit(1);
509 }
510
vobgroup_set_audio_attr(struct vobgroup * va,int attr,const char * s,int ch)511 static int vobgroup_set_audio_attr(struct vobgroup *va,int attr,const char *s,int ch)
512 {
513 if (ch >= va->numaudiotracks) /* assert ch = va->numaudiotracks + 1 */
514 va->numaudiotracks = ch + 1; /* new audio track */
515 return
516 audiodesc_set_audio_attr(&va->ad[ch], &va->adwarn[ch], attr, s);
517 } /*vobgroup_set_audio_attr*/
518
vobgroup_set_subpic_attr(struct vobgroup * va,int attr,const char * s,int ch)519 static int vobgroup_set_subpic_attr(struct vobgroup *va,int attr,const char *s,int ch)
520 /* sets the specified subpicture attribute (might be SPU_ANY) to the specified keyword value.
521 Returns 1 if the attribute was already set to a different value, else 0.
522 Aborts the program on an unrecognizable value. */
523 {
524 int w;
525
526 if (ch >= va->numsubpicturetracks) /* assert ch = va->numsubpicturetracks + 1 */
527 va->numsubpicturetracks = ch + 1;
528
529 if (ATTRMATCH(SPU_ANY)) {
530 w=scanandwarnupdate(&va->sp[ch].slangpresent,s,&va->spwarn[ch].slangpresent,"subpicture language",alangdesc);
531 /* fixme: note this lang/nolang setting can only be specified on the command line,
532 not the XML file */
533 if(w) return w-1;
534 }
535
536 if(ATTRMATCH(SPU_LANG) && 2==strlen(s)) {
537 w=warnupdate(&va->sp[ch].slangpresent,AL_LANG,&va->spwarn[ch].slangpresent,"subpicture language",alangdesc);
538 /* turn on lang */
539 if(va->sp[ch].lang[0] || va->sp[ch].lang[1])
540 w=1; /* language code already set */
541 va->sp[ch].lang[0]=tolower(s[0]); /* note I don't actually validate the language code */
542 va->sp[ch].lang[1]=tolower(s[1]);
543 return w;
544 }
545
546 if (ATTRMATCH(SPU_CONTENT))
547 {
548 w = scanandwarnupdate(&va->sp[ch].scontent, s, &va->spwarn[ch].scontent, "subpicture content type", scontentdesc);
549 if(w)
550 return w - 1;
551 } /*if*/
552 fprintf(stderr,"ERR: Cannot parse subpicture option '%s' on track %d\n",s,ch);
553 exit(1);
554 }
555
vobgroup_set_subpic_stream(struct vobgroup * va,int ch,const char * m,int id)556 static int vobgroup_set_subpic_stream(struct vobgroup *va, int ch, const char *m, int id)
557 /* adds a mapping for the subpicture stream numbered ch (in order of appearance) with
558 mode name m to the substream with ID id. */
559 {
560 int mid;
561 if (ch >= va->numsubpicturetracks) /* assert ch = va->numsubpicturetracks + 1 */
562 va->numsubpicturetracks = ch + 1;
563 mid = findsubpmode(m);
564 if (mid < 0)
565 {
566 fprintf(stderr, "ERR: Cannot parse subpicture stream mode '%s'\n", m);
567 exit(1);
568 } /*if*/
569 if (va->sp[ch].idmap[mid] && va->sp[ch].idmap[mid] != 128 + id)
570 {
571 fprintf(stderr, "ERR: Subpicture stream already defined for subpicture %d mode %s\n", ch, m);
572 exit(1);
573 } /*if*/
574 va->sp[ch].idmap[mid] = 128 + id;
575
576 return 0;
577 } /*vobgroup_set_subpic_stream*/
578
inferattr(int * a,int def)579 static void inferattr(int *a,int def)
580 /* defaults *a to def if not already set. */
581 {
582 if( a[0]!=0 ) return;
583 a[0]=def;
584 }
585
getsubpmask(const struct videodesc * vd)586 int getsubpmask(const struct videodesc *vd)
587 /* returns a 4-bit mask specifying the default usage of a subpicture stream, with
588 meaning as follows:
589 3 2 1 0
590 | | | \ narrowscreen footage
591 | | \widescreen footage, crop on narrowscreen display
592 | \widescreen footage, letterbox on narrowscreen display
593 \widescreen footage, pan&scan on narrowscreen display
594 */
595 {
596 int mask = 0;
597 if (vd->vaspect == VA_16x9)
598 mask |= 14; /* widescreen => allow pan&scan, letterbox and crop */
599 else
600 mask |= 1;
601 switch (vd->vwidescreen)
602 {
603 case VW_NOLETTERBOX:
604 mask &= -1-4; /* clear letterbox bit */
605 break;
606 case VW_NOPANSCAN:
607 mask &= -1-8; /* clear pan&scan bit */
608 break;
609 case VW_CROP:
610 mask |= 2; /* redundant? crop bit already set for widescreen */
611 break;
612 } /*switch*/
613 return
614 mask;
615 } /*getsubpmask*/
616
set_video_format_attr(struct vobgroup * va,vtypes pstype)617 static void set_video_format_attr
618 (
619 struct vobgroup * va,
620 vtypes pstype
621 )
622 {
623 #if defined(DEFAULT_VIDEO_FORMAT)
624 # if DEFAULT_VIDEO_FORMAT == 1
625 inferattr(&va->vd.vformat, default_video_format || VF_NTSC);
626 # elif DEFAULT_VIDEO_FORMAT == 2
627 inferattr(&va->vd.vformat, default_video_format || VF_PAL);
628 # endif
629 #else
630 inferattr(&va->vd.vformat, default_video_format);
631 if (va->vd.vformat == 0)
632 {
633 fprintf(stderr, "ERR: no video format specified for %s\n", pstypes[pstype]);
634 exit(1);
635 } /*if*/
636 #endif
637 } /*set_video_format_attr*/
638
setattr(struct vobgroup * va,vtypes pstype)639 static void setattr
640 (
641 struct vobgroup * va,
642 vtypes pstype
643 )
644 /* matches up video, audio and subpicture tracks that the user specified with
645 those actually found. */
646 {
647 int i;
648
649 /* warn user about defaulting settings not already determined */
650 if (va->vd.vmpeg == VM_NONE)
651 fprintf(stderr, "WARN: video mpeg version was not autodetected\n");
652 if (va->vd.vres == VS_NONE)
653 fprintf(stderr, "WARN: video resolution was not autodetected\n");
654 if (va->vd.vformat == VF_NONE)
655 fprintf(stderr, "WARN: video format was not autodetected\n");
656 if (va->vd.vaspect == VA_NONE)
657 fprintf(stderr, "WARN: aspect ratio was not autodetected\n");
658 /* default the undetermined settings */
659 inferattr(&va->vd.vmpeg, VM_MPEG2);
660 inferattr(&va->vd.vres, VS_720H);
661 set_video_format_attr(va, pstype);
662 inferattr(&va->vd.vaspect, VA_4x3);
663 /* check for incompatible combinations of aspect/widescreen settings */
664 if (va->vd.vaspect == VA_4x3)
665 {
666 if (va->vd.vwidescreen == VW_NOLETTERBOX || va->vd.vwidescreen == VW_NOPANSCAN)
667 {
668 fprintf
669 (
670 stderr,
671 "ERR: widescreen conversion should not be set to either noletterbox or"
672 " nopanscan for 4:3 source material.\n"
673 );
674 exit(1);
675 } /*if*/
676 }
677 else
678 {
679 if (va->vd.vwidescreen == VW_CROP)
680 {
681 fprintf
682 (
683 stderr,
684 "ERR: widescreen conversion should not be set to crop for 16:9 source material.\n"
685 );
686 exit(1);
687 } /*if*/
688 } /*if*/
689
690 for (i = 0; i < 32; i++) /* 8 audio streams * 4 formats */
691 {
692 /* collect all the appropriate audio tracks for this vobgroup, matching up
693 the descriptions specified by the user with those actually found in the
694 movie files */
695 const int id = (i >> 2) + 1;
696 const int afmt = (i & 3) + 1; // note this does not follow the normal stream order
697 /* afmt = 1 => AC3, 2 => MPEG audio, 3 => PCM, 4 => DTS, in order of preference */
698 int j;
699 bool fnd;
700 struct audchannel *fad = 0;
701 int matchidx, bestmatchcount;
702
703 fnd = false;
704 for (j = 0; j < va->numvobs; j++)
705 {
706 fad = &va->vobs[j]->audch[id - 1 + (afmt - 1) * 8];
707 if (fad->numaudpts) /* audio track actually present */
708 {
709 fnd = true;
710 break;
711 } /*if*/
712 } /*for*/
713 if (!fnd)
714 continue;
715
716 // do we already know about this stream?
717 fnd = false;
718 for (j = 0; j < va->numaudiotracks; j++)
719 if (va->ad[j].aformat == afmt && va->ad[j].aid == id)
720 {
721 fnd = true;
722 break;
723 } /*if; for*/
724 if (fnd)
725 continue;
726
727 bestmatchcount = -1;
728 matchidx = -1;
729 // maybe we know about this type of stream but haven't matched the id yet?
730 for (j = 0; j < va->numaudiotracks; j++)
731 if (va->ad[j].aid == 0)
732 {
733 int thismatchcount = 0; /* will exceed bestmatchcount first time, at least */
734 #define ACMPV(setting, val) \
735 /* how does va->ad[j] match fad->ad, not counting undetermined values? Let me count the ways... */ \
736 if (va->ad[j].setting != 0 && val != 0 && va->ad[j].setting != val) \
737 continue; /* mismatch on determined value */ \
738 if (va->ad[j].setting == val) \
739 thismatchcount++;
740 #define ACMP(setting) ACMPV(setting, fad->ad.setting)
741 ACMPV(aformat, afmt)
742 ACMP(aquant)
743 ACMP(adolby)
744 ACMP(achannels)
745 ACMP(asample)
746 #undef ACMP
747 #undef ACMPV
748 if (thismatchcount > bestmatchcount)
749 {
750 bestmatchcount = thismatchcount;
751 matchidx = j;
752 } /*if*/
753 fnd = true;
754 } /*if; for*/
755 if (!fnd)
756 {
757 // guess we need to add this stream
758 j = va->numaudiotracks++; /* new entry */
759 }
760 else
761 j = matchidx; /* replace previous best match */
762 va->ad[j].aformat = afmt;
763 va->ad[j].aid = id;
764 (void)warnupdate(&va->ad[j].aquant,
765 fad->ad.aquant,
766 &va->adwarn[j].aquant,
767 "audio quantization",
768 aquantdesc);
769 (void)warnupdate(&va->ad[j].adolby,
770 fad->ad.adolby,
771 &va->adwarn[j].adolby,
772 "surround",
773 adolbydesc);
774 (void)warnupdate(&va->ad[j].achannels,
775 fad->ad.achannels,
776 &va->adwarn[j].achannels,
777 "number of channels",
778 achanneldesc);
779 (void)warnupdate(&va->ad[j].asample,
780 fad->ad.asample,
781 &va->adwarn[j].asample,
782 "sampling rate",
783 asampledesc);
784 } /*for*/
785 for (i = 0; i < va->numaudiotracks; i++)
786 {
787 /* fill in the blanks in the tracks I've just collected */
788 if (va->ad[i].aformat == AF_NONE)
789 {
790 fprintf(stderr, "WARN: audio stream %d was not autodetected\n", i);
791 } /*if*/
792 inferattr(&va->ad[i].aformat, AF_MP2);
793 switch(va->ad[i].aformat)
794 {
795 case AF_AC3:
796 case AF_DTS:
797 inferattr(&va->ad[i].aquant, AQ_DRC);
798 inferattr(&va->ad[i].achannels, 6);
799 break;
800 case AF_MP2:
801 inferattr(&va->ad[i].aquant, AQ_20);
802 inferattr(&va->ad[i].achannels, 2);
803 break;
804 case AF_PCM:
805 inferattr(&va->ad[i].achannels, 2);
806 inferattr(&va->ad[i].aquant, AQ_16);
807 break;
808 } /*switch*/
809 inferattr(&va->ad[i].asample, AS_48KHZ);
810 } /*for*/
811
812 for (i = 0; i < va->numallpgcs; i++)
813 {
814 int j, k, l, m, mask;
815 bool used;
816 struct pgc * const pgc = va->allpgcs[i];
817
818 mask = getsubpmask(&va->vd);
819
820 // If any of the subpicture streams were manually set for this PGC, assume
821 // they all were set and don't try to infer anything (in case a VOB is used
822 // by multiple PGC's, but only some subpictures are exposed in one PGC and others
823 // in the other PGC)
824 for (m = 0; m < 4; m++)
825 if (pgc->subpmap[0][m])
826 goto noinfer;
827
828 for (j = 0; j < pgc->numsources; j++)
829 {
830 const struct vob * const vob = pgc->sources[j]->vob;
831
832 for (k = 0; k < 32; k++)
833 {
834 // Does this subpicture stream exist in the VOB? if not, skip
835 if (!vob->audch[k + 32].numaudpts)
836 continue;
837 // Is this subpicture stream already referenced by the subpicture table? if so, skip
838 for (l = 0; l < 32; l++)
839 for (m = 0; m < 4; m++)
840 if (pgc->subpmap[l][m] == 128 + k)
841 goto handled;
842 // Is this subpicture id defined by the vobgroup?
843 used = false;
844 for (l = 0; l < va->numsubpicturetracks; l++)
845 for (m = 0; m < 4; m++)
846 if (va->sp[l].idmap[m] == 128 + k && pgc->subpmap[l][m] == 0)
847 {
848 pgc->subpmap[l][m] = 128 + k;
849 used = true; // keep looping in case it's referenced multiple times
850 } /*if; for; for*/
851 if (used)
852 continue;
853 // Find a subpicture slot that is not used
854 for (l = 0; l < 32; l++)
855 {
856 // Is this slot defined by the vobgroup? If so, it can't be used
857 if (l < va->numsubpicturetracks)
858 {
859 for (m = 0; m < 4; m++)
860 if (va->sp[l].idmap[m])
861 goto next;
862 } /*if*/
863 // Is this slot defined by the pgc? If so, it can't be used
864 for (m = 0; m < 4; m++)
865 if (pgc->subpmap[l][m])
866 goto next;
867 break;
868 next: ;
869 } /*for*/
870 assert(l < 32);
871 // Set all the appropriate stream ids
872 for (m = 0; m < 4; m++)
873 pgc->subpmap[l][m] = (mask & 1 << m) != 0 ? 128 + k : 127;
874 handled: ;
875 } /*for*/
876 } /*for*/
877 noinfer:
878 for (m = 0; m < 4; m++)
879 {
880 if ((mask & 1 << m) != 0)
881 continue;
882 for (l = 0; l < 32; l++)
883 {
884 int mainid = -1;
885 for (m = 0; m < 4; m++)
886 {
887 if ((mask & 1 << m) == 0 && (pgc->subpmap[l][m] & 128) == 128)
888 {
889 fprintf
890 (
891 stderr,
892 "WARN: PGC %d has the subtitle set for stream %d, mode %s"
893 " which is illegal given the video characteristics. Forcibly"
894 " removing.\n",
895 i,
896 l,
897 smodedesc[m + 1]
898 );
899 pgc->subpmap[l][m] = 127;
900 } /*if*/
901 if (pgc->subpmap[l][m] & 128)
902 {
903 if (mainid == -1)
904 mainid = pgc->subpmap[l][m] & 127;
905 else if (mainid >= 0 && mainid != (pgc->subpmap[l][m] & 127))
906 mainid = -2;
907 } /*if*/
908 } /*for*/
909 // if no streams are set for this subpicture, ignore it
910 if (mainid == -1)
911 continue;
912 // if any streams aren't set that should be (because of the mask), then set them to the main stream
913 for (m = 0; m < 4; m++)
914 {
915 if ((mask & 1 << m) == 0)
916 continue;
917 if ((pgc->subpmap[l][m] & 128) == 0)
918 {
919 if (mainid < 0)
920 fprintf
921 (
922 stderr,
923 "WARN: Cannot infer the stream id for subpicture %d mode %s"
924 " in PGC %d; please manually specify.\n",
925 l,
926 smodedesc[m+1],
927 i
928 );
929 else
930 pgc->subpmap[l][m] = 128 + mainid;
931 } /*if*/
932 } /*for*/
933 } /*for*/
934 } /*for*/
935 } /*for*/
936
937 for (i = 0; i < 32; i++)
938 {
939 int j, k;
940 bool fnd;
941 fnd = false;
942 for (j = 0; j < va->numallpgcs; j++)
943 for (k = 0; k < 4; k++)
944 if (va->allpgcs[j]->subpmap[i][k])
945 fnd = true;
946 if (!fnd)
947 continue;
948 // guess we need to add this stream
949 if (i >= va->numsubpicturetracks)
950 va->numsubpicturetracks = i + 1;
951 } /*for*/
952
953 if (va->numsubpicturetracks > 1 && pstype != VTYPE_VTS)
954 {
955 fprintf
956 (
957 stderr,
958 "WARN: Too many subpicture tracks for a menu; 1 is allowed, %d are present."
959 " Perhaps you want different <stream> tags for normal/widescreen/letterbox/panscan"
960 " within one <subpicture> tag instead of multiple <subpicture> tags?\n",
961 va->numsubpicturetracks
962 );
963 } /*if*/
964
965 fprintf(stderr, "INFO: Generating %s with the following video attributes:\n", pstypes[pstype]);
966 fprintf(stderr, "INFO: MPEG version: %s\n", vmpegdesc[va->vd.vmpeg]);
967 fprintf(stderr, "INFO: TV standard: %s\n", vformatdesc[va->vd.vformat]);
968 fprintf(stderr, "INFO: Aspect ratio: %s\n", vaspectdesc[va->vd.vaspect]);
969 fprintf(stderr, "INFO: Resolution: %dx%d\n",
970 va->vd.vres != VS_720H ? (va->vd.vres == VS_704H ? 704 : 352) : 720,
971 (va->vd.vres == VS_352L ? 240 : 480) * (va->vd.vformat == VF_PAL ? 6 : 5) / 5);
972 for (i = 0; i < va->numaudiotracks; i++)
973 {
974 fprintf
975 (
976 stderr,
977 "INFO: Audio ch %d format: %s/%s, %s %s",
978 i,
979 aformatdesc[va->ad[i].aformat],
980 achanneldesc[va->ad[i].achannels],
981 asampledesc[va->ad[i].asample],
982 aquantdesc[va->ad[i].aquant]
983 );
984 if (va->ad[i].adolby == AD_SURROUND)
985 fprintf(stderr, ", surround");
986 if (va->ad[i].alangpresent == AL_LANG)
987 fprintf(stderr, ", '%c%c'", va->ad[i].lang[0], va->ad[i].lang[1]);
988 fprintf(stderr, "\n");
989 if (!va->ad[i].aid)
990 fprintf(stderr, "WARN: Audio ch %d is not used!\n", i);
991 } /*for*/
992 } /*setattr*/
993
findcellvobu(const struct vob * va,int cellid)994 int findcellvobu(const struct vob *va,int cellid)
995 /* finds the element of array va that includes the cell with ID cellid. */
996 {
997 int l=0,h=va->numvobus-1;
998 if( h<l )
999 return 0;
1000 cellid=(cellid&255)|(va->vobid*256);
1001 if( cellid<va->vobu[0].vobcellid )
1002 return 0;
1003 if( cellid>va->vobu[h].vobcellid )
1004 return h+1;
1005 while(l<h) { /* search by binary chop */
1006 int m=(l+h)/2;
1007 if( cellid<=va->vobu[m].vobcellid )
1008 h=m;
1009 else
1010 l=m+1;
1011 }
1012 return l;
1013 }
1014
getcellpts(const struct vob * va,int cellid)1015 pts_t getcellpts(const struct vob *va,int cellid)
1016 /* returns the duration of the specified cell. */
1017 {
1018 int s=findcellvobu(va,cellid),e=findcellvobu(va,cellid+1);
1019 if( s==e ) return 0;
1020 return va->vobu[e-1].sectpts[1]-va->vobu[s].sectpts[0];
1021 }
1022
findvobu(const struct vob * va,pts_t pts,int l,int h)1023 int findvobu(const struct vob *va,pts_t pts,int l,int h)
1024 /* finds the element of array va, within indexes l and h, that includes time pts. */
1025 {
1026 // int l=0,h=va->numvobus-1;
1027
1028 if( h<l )
1029 return l-1;
1030 if( pts<va->vobu[l].sectpts[0] )
1031 return l-1;
1032 if( pts>=va->vobu[h].sectpts[1] )
1033 return h+1;
1034 while(l<h) { /* search by binary chop */
1035 int m=(l+h+1)/2;
1036 if( pts < va->vobu[m].sectpts[0] )
1037 h=m-1;
1038 else
1039 l=m;
1040 }
1041 return l;
1042 }
1043
getptsspan(const struct pgc * ch)1044 pts_t getptsspan(const struct pgc *ch)
1045 /* returns the duration of the specified PGC. */
1046 {
1047 int s,c,ci;
1048 pts_t ptsspan=0;
1049
1050 for( s=0; s<ch->numsources; s++ ) {
1051 const struct source * const sc = ch->sources[s];
1052 for( c=0; c<sc->numcells; c++ ) {
1053 const struct cell *const cl = &sc->cells[c];
1054 for( ci=cl->scellid; ci<cl->ecellid; ci++ )
1055 ptsspan+=getcellpts(sc->vob,ci);
1056 }
1057 }
1058 return ptsspan;
1059 }
1060
makevtsdir(const char * s)1061 static char *makevtsdir(const char *s)
1062 /* returns the full pathname of the VIDEO_TS subdirectory within s if non-NULL,
1063 else returns NULL. */
1064 {
1065 static const char * subdir = "/VIDEO_TS";
1066 char * fbuf;
1067 if (s != NULL)
1068 {
1069 fbuf = malloc(strlen(s) + strlen(subdir) + 1);
1070 sprintf(fbuf, "%s%s", s, subdir);
1071 }
1072 else
1073 {
1074 fbuf = NULL;
1075 } /*if*/
1076 return
1077 fbuf;
1078 }
1079
1080 // jumppad requires the existence of a menu to operate
1081 // if no languages exist, create an english one
jp_force_menu(struct menugroup * mg,vtypes type)1082 static void jp_force_menu(struct menugroup *mg, vtypes type)
1083 {
1084 struct pgcgroup *pg;
1085
1086 if (!jumppad)
1087 return;
1088 if (mg->numgroups)
1089 return;
1090 fprintf(stderr, "WARN: The use of jumppad requires a menu; creating a dummy ENGLISH menu\n");
1091 pg = pgcgroup_new(type);
1092 menugroup_add_pgcgroup(mg, "en", pg);
1093 } /*jp_force_menu*/
1094
ScanIfo(struct toc_summary * ts,const char * ifo)1095 static void ScanIfo(struct toc_summary *ts, const char *ifo)
1096 /* scans another existing VTS IFO file and puts info about it
1097 into *ts for inclusion in the VMG. */
1098 {
1099 static unsigned char buf[2048];
1100 struct vtsdef *vd;
1101 int i,first;
1102 FILE * const h = fopen(ifo, "rb");
1103 if (!h)
1104 {
1105 fprintf(stderr, "ERR: cannot open %s: %s\n", ifo, strerror(errno));
1106 exit(1);
1107 } /*if*/
1108 if (ts->numvts + 1 >= MAXVTS)
1109 {
1110 /* shouldn't occur */
1111 fprintf(stderr,"ERR: Too many VTSs\n");
1112 exit(1);
1113 } /*if*/
1114 fread(buf, 1, 2048, h);
1115 vd = &ts->vts[ts->numvts]; /* where to put new entry */
1116 if (read4(buf + 0xc0) != 0) /* start sector of menu VOB */
1117 vd->hasmenu = true;
1118 else
1119 vd->hasmenu = false;
1120 vd->numsectors = read4(buf + 0xc) + 1; /* last sector of title set (last sector of BUP) */
1121 memcpy(vd->vtscat, buf + 0x22, 4); /* VTS category */
1122 memcpy(vd->vtssummary, buf + 0x100, 0x300); /* attributes of streams in VTS and VTSM */
1123 fread(buf, 1, 2048, h); // VTS_PTT_SRPT is 2nd sector
1124 // we only need to read the 1st sector of it because we only need the
1125 // pgc pointers
1126 vd->numtitles = read2(buf); /* nr titles */
1127 vd->numchapters = (int *)malloc(sizeof(int) * vd->numtitles);
1128 /* array of nr chapters in each title */
1129 first = 8 + vd->numtitles * 4; /* offset to VTS_PTT for first title */
1130 for (i = 0; i < vd->numtitles - 1; i++)
1131 {
1132 const int n = read4(buf + 12 + i * 4); /* offset to VTS_PTT for next title */
1133 vd->numchapters[i] = (n - first) / 4; /* difference from previous offset gives nr chapters for this title */
1134 first = n;
1135 } /*for*/
1136 vd->numchapters[i] = (read4(buf + 4) /* end address (last byte of last VTS_PTT) */ + 1 - first) / 4;
1137 /* nr chapters for last title */
1138 fclose(h);
1139 ts->numvts++;
1140 } /*ScanIfo*/
1141
forceaddentry(struct pgcgroup * va,int entry)1142 static void forceaddentry(struct pgcgroup *va, int entry)
1143 /* gives the first PGC in va the specified entry type, if this is not present already. */
1144 {
1145 if (!va->numpgcs && !jumppad)
1146 return;
1147 if (!(va->allentries & entry)) /* entry not already present */
1148 {
1149 if (va->numpgcs)
1150 va->pgcs[0]->entries |= entry;
1151 va->allentries |= entry;
1152 va->numentries++;
1153 } /*if*/
1154 } /*forceaddentry*/
1155
checkaddentry(struct pgcgroup * va,int entry)1156 static void checkaddentry(struct pgcgroup *va, int entry)
1157 /* gives the first PGC in va the specified entry type, if this is not present already
1158 and it has at least one PGC. */
1159 {
1160 if (va->numpgcs)
1161 forceaddentry(va, entry);
1162 } /*checkaddentry*/
1163
getvtsnum(const char * fbase)1164 static int getvtsnum(const char *fbase)
1165 /* returns the next unused titleset number within output directory fbase. */
1166 {
1167 static char realfbase[1000];
1168 int i;
1169 if (!fbase)
1170 return 1;
1171 for (i = 1; i <= 99; i++)
1172 {
1173 FILE *h;
1174 snprintf(realfbase, sizeof realfbase, "%s/VIDEO_TS/VTS_%02d_0.IFO", fbase, i);
1175 h = fopen(realfbase, "rb");
1176 if (!h)
1177 break; /* doesn't look like this number is in use */
1178 fclose(h);
1179 } /*for*/
1180 fprintf(stderr, "STAT: Picking VTS %02d\n", i);
1181 return i;
1182 } /*getvtsnum*/
1183
deletedir(const char * fbase)1184 static void deletedir(const char * fbase)
1185 /* deletes any existing output directory structure. Note for safety I only look for
1186 names matching limited patterns. */
1187 {
1188 static char dirname[1000], subname[1000];
1189 DIR * subdir;
1190 snprintf(dirname, sizeof dirname, "%s/VIDEO_TS", fbase);
1191 subdir = opendir(dirname);
1192 if (subdir == NULL && errno != ENOENT)
1193 {
1194 fprintf(stderr, "ERR: cannot open dir for deleting %s: %s\n", dirname, strerror(errno));
1195 exit(1);
1196 } /*if*/
1197 if (subdir != NULL)
1198 {
1199 for (;;)
1200 {
1201 const struct dirent * const entry = readdir(subdir);
1202 if (entry == NULL)
1203 break;
1204 if
1205 (
1206 strlen(entry->d_name) == 12
1207 &&
1208 entry->d_name[8] == '.'
1209 &&
1210 (
1211 !strcmp(entry->d_name + 9, "IFO")
1212 ||
1213 !strcmp(entry->d_name + 9, "BUP")
1214 ||
1215 !strcmp(entry->d_name + 9, "VOB")
1216 )
1217 &&
1218 (
1219 !strncmp(entry->d_name, "VIDEO_TS", 8)
1220 ||
1221 !strncmp(entry->d_name, "VTS_", 4)
1222 &&
1223 entry->d_name[6] == '_'
1224 )
1225 )
1226 {
1227 snprintf(subname, sizeof subname, "%s/%s", dirname, entry->d_name);
1228 if (unlink(subname))
1229 {
1230 fprintf(stderr, "ERR: cannot delete file %s: %s\n", subname, strerror(errno));
1231 exit(1);
1232 } /*if*/
1233 } /*if*/
1234 } /*for*/
1235 closedir(subdir);
1236 } /*if*/
1237 if (rmdir(dirname) && errno != ENOENT)
1238 {
1239 fprintf(stderr, "ERR: cannot delete dir %s: %s\n", dirname, strerror(errno));
1240 exit(1);
1241 } /*if*/
1242 snprintf(dirname, sizeof dirname, "%s/AUDIO_TS", fbase);
1243 if (rmdir(dirname) && errno != ENOENT)
1244 {
1245 fprintf(stderr, "ERR: cannot delete dir %s: %s\n", dirname, strerror(errno));
1246 exit(1);
1247 } /*if*/
1248 errno = 0;
1249 } /*deletedir*/
1250
initdir(const char * fbase)1251 static void initdir(const char * fbase)
1252 /* creates the top-level DVD-video subdirectories within the output directory,
1253 if they don't already exist. */
1254 {
1255 static char realfbase[1000];
1256 if (fbase)
1257 {
1258 if (delete_output_dir)
1259 {
1260 deletedir(fbase);
1261 delete_output_dir = false; /* only do on first call */
1262 } /*if*/
1263 if (mkdir(fbase, 0777) && errno != EEXIST)
1264 {
1265 fprintf(stderr, "ERR: cannot create dir %s: %s\n", fbase, strerror(errno));
1266 exit(1);
1267 } /*if*/
1268 snprintf(realfbase, sizeof realfbase, "%s/VIDEO_TS", fbase);
1269 if (mkdir(realfbase, 0777) && errno != EEXIST)
1270 {
1271 fprintf(stderr, "ERR: cannot create dir %s: %s\n", realfbase, strerror(errno));
1272 exit(1);
1273 } /*if*/
1274 snprintf(realfbase, sizeof realfbase, "%s/AUDIO_TS", fbase);
1275 if (mkdir(realfbase, 0777) && errno != EEXIST)
1276 {
1277 fprintf(stderr, "ERR: cannot create dir %s: %s\n", realfbase, strerror(errno));
1278 exit(1);
1279 } /*if*/
1280 } /*if*/
1281 errno = 0;
1282 } /*initdir*/
1283
colorinfo_new()1284 static struct colorinfo *colorinfo_new()
1285 {
1286 struct colorinfo *ci=malloc(sizeof(struct colorinfo));
1287 ci->refcount=1;
1288 memcpy(ci->color,default_colors,16*sizeof(int));
1289 return ci;
1290 }
1291
colorinfo_free(struct colorinfo * ci)1292 static void colorinfo_free(struct colorinfo *ci)
1293 {
1294 if (ci)
1295 {
1296 ci->refcount--;
1297 if (!ci->refcount)
1298 free(ci);
1299 } /*if*/
1300 } /*colorinfo_free*/
1301
vob_new(const char * fname,struct pgc * progchain)1302 static struct vob *vob_new(const char *fname,struct pgc *progchain)
1303 {
1304 struct vob *v=malloc(sizeof(struct vob));
1305 memset(v,0,sizeof(struct vob));
1306 v->fname=strdup(fname);
1307 v->progchain=progchain;
1308 return v;
1309 }
1310
vob_free(struct vob * v)1311 static void vob_free(struct vob *v)
1312 {
1313 if (v)
1314 {
1315 int i;
1316 free(v->fname);
1317 free(v->vobu);
1318 for (i = 0; i < 64; i++)
1319 free(v->audch[i].audpts);
1320 free(v);
1321 } /*if*/
1322 } /*vob_free*/
1323
vobgroup_new()1324 static struct vobgroup *vobgroup_new()
1325 {
1326 struct vobgroup *vg=malloc(sizeof(struct vobgroup));
1327 memset(vg,0,sizeof(struct vobgroup));
1328 return vg;
1329 }
1330
vobgroup_free(struct vobgroup * vg)1331 static void vobgroup_free(struct vobgroup *vg)
1332 {
1333 if (vg)
1334 {
1335 int i;
1336 free(vg->allpgcs);
1337 if (vg->vobs)
1338 {
1339 for (i = 0; i < vg->numvobs; i++)
1340 vob_free(vg->vobs[i]);
1341 free(vg->vobs);
1342 } /*if*/
1343 free(vg);
1344 } /*if*/
1345 } /*vobgroup_free*/
1346
vobgroup_addvob(struct vobgroup * pg,struct pgc * p,struct source * s)1347 static void vobgroup_addvob(struct vobgroup *pg, struct pgc *p, struct source *s)
1348 {
1349 const bool forcenew = p->numbuttons != 0;
1350 if (!forcenew)
1351 {
1352 /* Reuse a previously-created vob element with the same input file name,
1353 if one can be found. This is not tried if buttons are present--is that
1354 because of colour-remapping issues? */
1355 int i;
1356 for (i = 0; i < pg->numvobs; i++)
1357 if (!strcmp(pg->vobs[i]->fname, s->fname) && pg->vobs[i]->progchain->numbuttons == 0)
1358 {
1359 s->vob = pg->vobs[i];
1360 return;
1361 } /*if*/
1362 } /*if*/
1363 pg->vobs = realloc(pg->vobs, (pg->numvobs + 1) * sizeof(struct vob *));
1364 s->vob = pg->vobs[pg->numvobs++] = vob_new(s->fname, p);
1365 } /*vobgroup_addvob*/
1366
pgcgroup_pushci(struct pgcgroup * p,bool warn)1367 static void pgcgroup_pushci(struct pgcgroup *p, bool warn)
1368 /* shares colorinfo structures among all pgc elements that have sources
1369 which were allocated the same vob structures. */
1370 {
1371 int i, j, ii, jj;
1372 for (i = 0; i < p->numpgcs; i++)
1373 {
1374 if (!p->pgcs[i]->colors)
1375 continue;
1376 for (j = 0; j < p->pgcs[i]->numsources; j++)
1377 {
1378 const struct vob * const v = p->pgcs[i]->sources[j]->vob;
1379 for (ii = 0; ii < p->numpgcs; ii++)
1380 for (jj = 0; jj < p->pgcs[ii]->numsources; jj++)
1381 if (v == p->pgcs[ii]->sources[jj]->vob)
1382 {
1383 if (!p->pgcs[ii]->colors)
1384 {
1385 p->pgcs[ii]->colors = p->pgcs[i]->colors;
1386 p->pgcs[ii]->colors->refcount++;
1387 }
1388 else if (p->pgcs[ii]->colors != p->pgcs[i]->colors && warn)
1389 {
1390 fprintf
1391 (
1392 stderr,
1393 "WARN: Conflict in colormap between PGC %d and %d\n",
1394 i, ii
1395 );
1396 } /*if*/
1397 } /*if; for; for*/
1398 } /*for*/
1399 } /*for*/
1400 } /*pgcgroup_pushci*/
1401
pgcgroup_createvobs(struct pgcgroup * p,struct vobgroup * v)1402 static void pgcgroup_createvobs(struct pgcgroup *p, struct vobgroup *v)
1403 /* appends p->pgcs onto v->allpgcs and builds the struct vob arrays in the vobgroups. */
1404 {
1405 int i, j;
1406 v->allpgcs = (struct pgc **)realloc(v->allpgcs, (v->numallpgcs + p->numpgcs) * sizeof(struct pgc *));
1407 memcpy(v->allpgcs + v->numallpgcs, p->pgcs, p->numpgcs * sizeof(struct pgc *));
1408 v->numallpgcs += p->numpgcs;
1409 for (i = 0; i < p->numpgcs; i++)
1410 for (j = 0; j < p->pgcs[i]->numsources; j++)
1411 vobgroup_addvob(v, p->pgcs[i], p->pgcs[i]->sources[j]);
1412 pgcgroup_pushci(p, false);
1413 for (i = 0; i < p->numpgcs; i++)
1414 if (!p->pgcs[i]->colors)
1415 {
1416 p->pgcs[i]->colors = colorinfo_new();
1417 pgcgroup_pushci(p, false);
1418 } /*if; for*/
1419 pgcgroup_pushci(p, true);
1420 } /*pgcgroup_createvobs*/
1421
validatesummary(struct pgcgroup * va)1422 static void validatesummary(struct pgcgroup *va)
1423 /* merges the info for all pgcs and validates the collected settings for a pgcgroup. */
1424 {
1425 int i,allowedentries;
1426 bool err = false;
1427
1428 switch (va->pstype)
1429 {
1430 case VTYPE_VTSM:
1431 allowedentries = 0xf8; /* all entry menu types allowed except title */
1432 break;
1433 case VTYPE_VMGM:
1434 allowedentries = 4; /* title entry menu type only */
1435 break;
1436 case VTYPE_VTS:
1437 default:
1438 allowedentries = 0; /* no entry menu types */
1439 break;
1440 } /*switch*/
1441
1442 for( i=0; i<va->numpgcs; i++ ) {
1443 struct pgc *p=va->pgcs[i];
1444 /* why is this being done? let user specify it if they want
1445 if( !p->posti && p->numsources ) {
1446 struct source *s=p->sources[p->numsources-1];
1447 s->cells[s->numcells-1].pauselen=255;
1448 } */
1449 if( va->allentries & p->entries ) {
1450 /* this pgc adds entry menus already seen */
1451 int j;
1452
1453 for( j=0; j<8; j++ )
1454 if( va->allentries & p->entries & (1<<j) )
1455 fprintf(stderr,"ERR: Multiple definitions for entry %s, 2nd occurrence in PGC #%d\n",entries[j],i);
1456 err = true;
1457 }
1458 if (va->pstype != VTYPE_VTS && (p->entries & ~allowedentries) != 0)
1459 {
1460 /* disallowed entry menu types present--report them */
1461 int j;
1462 for (j = 0; j < 8; j++)
1463 if (p->entries & (~allowedentries) & (1 << j))
1464 fprintf
1465 (
1466 stderr,
1467 "ERR: Entry %s is not allowed for menu type %s\n",
1468 entries[j],
1469 pstypes[va->pstype]
1470 );
1471 err = true;
1472 } /*if*/
1473 va->allentries|=p->entries;
1474 if( p->numsources ) {
1475 int j;
1476 bool first;
1477 first = true;
1478 for( j=0; j<p->numsources; j++ ) {
1479 if( !p->sources[j]->numcells )
1480 fprintf(stderr,"WARN: Source has no cells (%s) in PGC %d\n",p->sources[j]->fname,i);
1481 else if( first ) {
1482 if( p->sources[j]->cells[0].ischapter!=CELL_CHAPTER_PROGRAM ) {
1483 fprintf(stderr,"WARN: First cell is not marked as a chapter in PGC %d, setting chapter flag\n",i);
1484 p->sources[j]->cells[0].ischapter=CELL_CHAPTER_PROGRAM;
1485 }
1486 first = false;
1487 }
1488 }
1489 }
1490 }
1491 for( i=1; i<256; i<<=1 )
1492 if( va->allentries&i )
1493 va->numentries++;
1494 if(err)
1495 exit(1);
1496 }
1497
statement_free(struct vm_statement * s)1498 static void statement_free(struct vm_statement *s)
1499 {
1500 if (s)
1501 {
1502 free(s->s1);
1503 free(s->s2);
1504 free(s->s3);
1505 free(s->s4);
1506 statement_free(s->param);
1507 statement_free(s->next);
1508 free(s);
1509 } /*if*/
1510 } /*statement_free*/
1511
source_new()1512 struct source *source_new()
1513 {
1514 struct source *v=malloc(sizeof(struct source));
1515 memset(v,0,sizeof(struct source));
1516 return v;
1517 }
1518
source_free(struct source * s)1519 static void source_free(struct source *s)
1520 {
1521 if (s)
1522 {
1523 int i;
1524 free(s->fname);
1525 if (s->cells)
1526 {
1527 for (i = 0; i < s->numcells; i++)
1528 statement_free(s->cells[i].commands);
1529 free(s->cells);
1530 } /*if*/
1531 // vob is a reference created by vobgroup_addvob
1532 free(s);
1533 } /*if*/
1534 } /*source_free*/
1535
source_add_cell(struct source * v,double starttime,double endtime,cell_chapter_types chap,int pause,const char * cmd)1536 int source_add_cell(struct source *v,double starttime,double endtime,cell_chapter_types chap,int pause,const char *cmd)
1537 {
1538 struct cell *c;
1539
1540 v->cells=realloc(v->cells,(v->numcells+1)*sizeof(struct cell)); /* space for a new cell */
1541 c=v->cells+v->numcells; /* the newly-added cell */
1542 v->numcells++;
1543 c->startpts=starttime*90000+.5;
1544 c->endpts=endtime*90000+.5;
1545 c->ischapter=chap;
1546 c->pauselen=pause;
1547 if( cmd )
1548 c->commands=vm_parse(cmd);
1549 else
1550 c->commands=0;
1551 return 0;
1552 }
1553
source_set_filename(struct source * v,const char * s)1554 void source_set_filename(struct source *v,const char *s)
1555 {
1556 v->fname=strdup(s);
1557 }
1558
button_freecontents(struct button * b)1559 static void button_freecontents(struct button *b)
1560 {
1561 if (b)
1562 {
1563 int i;
1564 free(b->name);
1565 statement_free(b->commands);
1566 for (i = 0; i < b->numstream; i++)
1567 {
1568 free(b->stream[i].up);
1569 free(b->stream[i].down);
1570 free(b->stream[i].left);
1571 free(b->stream[i].right);
1572 } /*for*/
1573 } /*if*/
1574 } /*button_freecontents*/
1575
pgc_new()1576 struct pgc *pgc_new()
1577 {
1578 struct pgc *p=malloc(sizeof(struct pgc));
1579 memset(p,0,sizeof(struct pgc));
1580 return p;
1581 }
1582
pgc_free(struct pgc * p)1583 void pgc_free(struct pgc *p)
1584 {
1585 if (p)
1586 {
1587 int i;
1588 if (p->sources)
1589 {
1590 for (i = 0; i < p->numsources; i++)
1591 source_free(p->sources[i]);
1592 free(p->sources);
1593 } /*if*/
1594 if (p->buttons)
1595 {
1596 for (i = 0; i < p->numbuttons; i++)
1597 button_freecontents(p->buttons + i);
1598 free(p->buttons);
1599 } /*if*/
1600 statement_free(p->prei);
1601 statement_free(p->posti);
1602 colorinfo_free(p->colors);
1603 // don't free the pgcgroup; it's an upward reference
1604 free(p);
1605 } /*if*/
1606 } /*pgc_free*/
1607
pgc_set_pre(struct pgc * p,const char * cmd)1608 void pgc_set_pre(struct pgc *p,const char *cmd)
1609 {
1610 assert(!p->prei);
1611 p->prei=vm_parse(cmd); // this will initialize prei
1612 }
1613
pgc_set_post(struct pgc * p,const char * cmd)1614 void pgc_set_post(struct pgc *p,const char *cmd)
1615 {
1616 assert(!p->posti);
1617 p->posti=vm_parse(cmd); // this will initialize posti
1618 }
1619
pgc_set_color(struct pgc * p,int index,int color)1620 void pgc_set_color(struct pgc *p,int index,int color)
1621 {
1622 assert(index>=0 && index<16);
1623 if( !p->colors ) p->colors=colorinfo_new();
1624 p->colors->color[index]=color;
1625 }
1626
pgc_set_stilltime(struct pgc * p,int still)1627 void pgc_set_stilltime(struct pgc *p,int still)
1628 {
1629 p->pauselen=still;
1630 }
1631
pgc_set_subpic_stream(struct pgc * p,int ch,const char * m,int id)1632 int pgc_set_subpic_stream(struct pgc *p,int ch,const char *m,int id)
1633 /* adds a mapping for the subpicture stream numbered ch (in order of appearance) with
1634 mode name m to the substream with ID id. */
1635 {
1636 int mid;
1637
1638 mid=findsubpmode(m);
1639 if( mid<0 ) {
1640 fprintf(stderr,"ERR: Cannot parse subpicture stream mode '%s'\n",m);
1641 exit(1);
1642 }
1643
1644 if( p->subpmap[ch][mid] && p->subpmap[ch][mid]!=128+id ) {
1645 fprintf(stderr,"ERR: Subpicture stream already defined for subpicture %d mode %s\n",ch,m);
1646 exit(1);
1647 }
1648 p->subpmap[ch][mid]=128+id;
1649
1650 return 0;
1651 }
1652
pgc_add_entry(struct pgc * p,vtypes vtype,const char * entry)1653 void pgc_add_entry(struct pgc *p, vtypes vtype, const char *entry)
1654 {
1655 int i;
1656 if (vtype != VTYPE_VTS)
1657 {
1658 for (i = 2; i < 8; i++)
1659 if (!strcasecmp(entry, entries[i]))
1660 {
1661 int v = 1 << i;
1662 if (p->entries & v)
1663 {
1664 fprintf
1665 (
1666 stderr,
1667 "ERR: Defined entry '%s' multiple times for the same PGC\n",
1668 entry
1669 );
1670 exit(1);
1671 } /*if*/
1672 p->entries |= 1 << i;
1673 return;
1674 } /*if*/
1675 }
1676 else
1677 {
1678 if (!strcasecmp(entry, "notitle"))
1679 {
1680 p->entries |= 1; /* anything nonzero */
1681 return;
1682 } /*if*/
1683 } /*if*/
1684 fprintf(stderr,"ERR: Unknown entry '%s'\n",entry);
1685 exit(1);
1686 } /*pgc_add_entry*/
1687
pgc_add_source(struct pgc * p,struct source * v)1688 void pgc_add_source(struct pgc *p,struct source *v)
1689 {
1690 if( !v->fname ) {
1691 fprintf(stderr,"ERR: source has no filename\n");
1692 exit(1);
1693 }
1694 p->sources=(struct source **)realloc(p->sources,(p->numsources+1)*sizeof(struct source *));
1695 p->sources[p->numsources++]=v;
1696 }
1697
pgc_add_button(struct pgc * p,const char * name,const char * cmd)1698 int pgc_add_button(struct pgc *p,const char *name,const char *cmd)
1699 /* adds a new button definition, optional name, and associated commands to a PGC. */
1700 {
1701 struct button *bs;
1702 if (p->numbuttons == 36)
1703 {
1704 fprintf(stderr, "ERR: Limit of up to 36 buttons\n");
1705 exit(1);
1706 } /*if*/
1707 p->buttons = (struct button *)realloc(p->buttons, (p->numbuttons + 1) * sizeof(struct button));
1708 bs = &p->buttons[p->numbuttons++];
1709 memset(bs, 0, sizeof(struct button));
1710 /* note stream-specific info (including spatial and auto-action info) is initially empty */
1711 if (name)
1712 bs->name = strdup(name);
1713 else
1714 {
1715 /* make up a sequentially-assigned name */
1716 char nm[10];
1717 snprintf(nm, sizeof nm, "%d", p->numbuttons);
1718 bs->name = strdup(nm);
1719 } /*if*/
1720 bs->commands = vm_parse(cmd);
1721 return 0;
1722 } /*pgc_add_button*/
1723
pgcgroup_new(vtypes type)1724 struct pgcgroup *pgcgroup_new(vtypes type)
1725 {
1726 struct pgcgroup *ps=malloc(sizeof(struct pgcgroup));
1727 memset(ps,0,sizeof(struct pgcgroup));
1728 ps->pstype=type;
1729 if (type == VTYPE_VTS)
1730 ps->pg_vg = vobgroup_new();
1731 return ps;
1732 }
1733
pgcgroup_free(struct pgcgroup * pg)1734 void pgcgroup_free(struct pgcgroup *pg)
1735 {
1736 if (pg)
1737 {
1738 int i;
1739 if (pg->pgcs)
1740 {
1741 for (i = 0; i < pg->numpgcs; i++)
1742 pgc_free(pg->pgcs[i]);
1743 free(pg->pgcs);
1744 } /*if*/
1745 vobgroup_free(pg->pg_vg);
1746 free(pg);
1747 } /*if*/
1748 } /*pgcgroup_free*/
1749
pgcgroup_add_pgc(struct pgcgroup * ps,struct pgc * p)1750 void pgcgroup_add_pgc(struct pgcgroup *ps,struct pgc *p)
1751 /* adds a new PGC to the specified pgcgroup. */
1752 {
1753 ps->pgcs = (struct pgc **)realloc(ps->pgcs, (ps->numpgcs + 1) * sizeof(struct pgc *));
1754 ps->pgcs[ps->numpgcs++] = p;
1755 assert(p->pgcgroup == 0); /* should not already be in any group! */
1756 p->pgcgroup = ps;
1757 } /*pgcgroup_add_pgc*/
1758
pgcgroup_set_video_attr(struct pgcgroup * va,int attr,const char * s)1759 int pgcgroup_set_video_attr(struct pgcgroup *va,int attr,const char *s)
1760 {
1761 return vobgroup_set_video_attr(va->pg_vg,attr,s);
1762 }
1763
pgcgroup_set_audio_attr(struct pgcgroup * va,int attr,const char * s,int ch)1764 int pgcgroup_set_audio_attr(struct pgcgroup *va,int attr,const char *s,int ch)
1765 {
1766 return vobgroup_set_audio_attr(va->pg_vg,attr,s,ch);
1767 }
1768
pgcgroup_set_subpic_attr(struct pgcgroup * va,int attr,const char * s,int ch)1769 int pgcgroup_set_subpic_attr(struct pgcgroup *va,int attr,const char *s,int ch)
1770 {
1771 return vobgroup_set_subpic_attr(va->pg_vg,attr,s,ch);
1772 }
1773
pgcgroup_set_subpic_stream(struct pgcgroup * va,int ch,const char * m,int id)1774 int pgcgroup_set_subpic_stream(struct pgcgroup *va,int ch,const char *m,int id)
1775 {
1776 return vobgroup_set_subpic_stream(va->pg_vg,ch,m,id);
1777 }
1778
menugroup_new()1779 struct menugroup *menugroup_new()
1780 {
1781 struct menugroup *mg=malloc(sizeof(struct menugroup));
1782 memset(mg,0,sizeof(struct menugroup));
1783 mg->mg_vg = vobgroup_new();
1784 return mg;
1785 }
1786
menugroup_free(struct menugroup * mg)1787 void menugroup_free(struct menugroup *mg)
1788 {
1789 if (mg)
1790 {
1791 int i;
1792 if (mg->groups)
1793 {
1794 for (i = 0; i < mg->numgroups; i++)
1795 pgcgroup_free(mg->groups[i].pg);
1796 free(mg->groups);
1797 } /*if*/
1798 vobgroup_free(mg->mg_vg);
1799 free(mg);
1800 } /*if*/
1801 } /*menugroup_free*/
1802
menugroup_add_pgcgroup(struct menugroup * mg,const char * lang,struct pgcgroup * pg)1803 void menugroup_add_pgcgroup(struct menugroup *mg, const char *lang, struct pgcgroup *pg)
1804 {
1805 mg->groups = (struct langgroup *)realloc(mg->groups, (mg->numgroups + 1) * sizeof(struct langgroup));
1806 if (strlen(lang) != 2)
1807 {
1808 fprintf(stderr, "ERR: Menu language '%s' is not two letters.\n", lang);
1809 exit(1);
1810 } /*if*/
1811 /* fixme: I don't check if there's already a langgroup with this language code? */
1812 mg->groups[mg->numgroups].lang[0] = tolower(lang[0]);
1813 mg->groups[mg->numgroups].lang[1] = tolower(lang[1]);
1814 mg->groups[mg->numgroups].lang[2] = 0;
1815 mg->groups[mg->numgroups].pg = pg;
1816 mg->numgroups++;
1817 } /*menugroup_add_pgcgroup*/
1818
menugroup_set_video_attr(struct menugroup * va,int attr,const char * s)1819 int menugroup_set_video_attr(struct menugroup *va,int attr,const char *s)
1820 {
1821 return vobgroup_set_video_attr(va->mg_vg,attr,s);
1822 }
1823
menugroup_set_audio_attr(struct menugroup * va,int attr,const char * s,int ch)1824 int menugroup_set_audio_attr(struct menugroup *va,int attr,const char *s,int ch)
1825 {
1826 return vobgroup_set_audio_attr(va->mg_vg,attr,s,ch);
1827 }
1828
menugroup_set_subpic_attr(struct menugroup * va,int attr,const char * s,int ch)1829 int menugroup_set_subpic_attr(struct menugroup *va,int attr,const char *s,int ch)
1830 {
1831 return vobgroup_set_subpic_attr(va->mg_vg,attr,s,ch);
1832 }
1833
menugroup_set_subpic_stream(struct menugroup * va,int ch,const char * m,int id)1834 int menugroup_set_subpic_stream(struct menugroup *va,int ch,const char *m,int id)
1835 {
1836 return vobgroup_set_subpic_stream(va->mg_vg,ch,m,id);
1837 }
1838
dvdauthor_enable_jumppad()1839 void dvdauthor_enable_jumppad()
1840 {
1841 if( allowallreg ) {
1842 fprintf(stderr,"ERR: Cannot enable both allgprm and jumppad\n");
1843 exit(1);
1844 }
1845 jumppad = true;
1846 }
1847
dvdauthor_enable_allgprm()1848 void dvdauthor_enable_allgprm()
1849 {
1850 if( jumppad ) {
1851 fprintf(stderr,"ERR: Cannot enable both allgprm and jumppad\n");
1852 exit(1);
1853 }
1854 allowallreg = true;
1855 }
1856
dvdauthor_vmgm_gen(struct pgc * fpc,struct menugroup * menus,const char * fbase)1857 void dvdauthor_vmgm_gen(struct pgc *fpc, struct menugroup *menus, const char *fbase)
1858 /* generates the VMG, taking into account all already-generated titlesets. */
1859 {
1860 DIR *d;
1861 struct dirent *de;
1862 char *vtsdir;
1863 int i;
1864 static struct toc_summary ts; /* static avoids having to initialize it! */
1865 static char fbuf[1000];
1866 static char ifonames[101][14];
1867 struct workset ws;
1868
1869 if (!fbase) // can't really make a vmgm without titlesets
1870 return;
1871 ws.titlesets = &ts;
1872 ws.menus = menus;
1873 ws.titles = 0;
1874 jp_force_menu(menus, VTYPE_VMGM);
1875 for (i = 0; i < menus->numgroups; i++)
1876 {
1877 validatesummary(menus->groups[i].pg);
1878 pgcgroup_createvobs(menus->groups[i].pg, menus->mg_vg);
1879 forceaddentry(menus->groups[i].pg, 4); /* entry=title */
1880 } /*for*/
1881 fprintf(stderr, "INFO: dvdauthor creating table of contents\n");
1882 initdir(fbase);
1883 // create base entry, if not already existing
1884 memset(&ts, 0, sizeof(struct toc_summary));
1885 vtsdir = makevtsdir(fbase);
1886 for (i = 0; i < 101; i++)
1887 ifonames[i][0] = 0; /* mark all name entries as unused */
1888 d = opendir(vtsdir);
1889 while ((de = readdir(d)) != 0)
1890 {
1891 /* look for existing titlesets */
1892 i = strlen(de->d_name);
1893 if
1894 (
1895 i == 12
1896 &&
1897 !strcasecmp(de->d_name + i - 6, "_0.IFO")
1898 &&
1899 !strncasecmp(de->d_name, "VTS_", 4)
1900 /* name is of form VTS_nn_0.IFO */
1901 )
1902 {
1903 i = (de->d_name[4] - '0') * 10 + (de->d_name[5] - '0');
1904 if (ifonames[i][0]) /* title set nr already seen!? will actually never happen */
1905 {
1906 fprintf(stderr, "ERR: Two different names for the same titleset: %s and %s\n",
1907 ifonames[i], de->d_name);
1908 exit(1);
1909 } /*if*/
1910 if (!i)
1911 {
1912 fprintf(stderr,"ERR: Cannot have titleset #0 (%s)\n", de->d_name);
1913 exit(1);
1914 } /*if*/
1915 strcpy(ifonames[i], de->d_name);
1916 } /*if*/
1917 } /*while*/
1918 closedir(d);
1919 for (i = 1; i <= 99; i++)
1920 {
1921 if (!ifonames[i][0])
1922 break;
1923 snprintf(fbuf, sizeof fbuf, "%s/%s", vtsdir, ifonames[i]);
1924 fprintf(stderr, "INFO: Scanning %s\n",fbuf);
1925 ScanIfo(&ts, fbuf); /* collect info about existing titleset for inclusion in new VMG IFO */
1926 } /*for*/
1927 for (; i <= 99; i++) /* look for discontiguously-assigned title nrs (error) */
1928 if (ifonames[i][0])
1929 {
1930 fprintf(stderr, "ERR: Titleset #%d (%s) does not immediately follow the last titleset\n",i,ifonames[i]);
1931 exit(1);
1932 } /*if; for*/
1933 if (!ts.numvts)
1934 {
1935 fprintf(stderr, "ERR: No .IFO files to process\n");
1936 exit(1);
1937 } /*if*/
1938 if (menus->mg_vg->numvobs != 0)
1939 {
1940 fprintf(stderr, "INFO: Creating menu for TOC\n");
1941 snprintf(fbuf, sizeof fbuf, "%s/VIDEO_TS.VOB", vtsdir);
1942 FindVobus(fbuf, menus->mg_vg, VTYPE_VMGM);
1943 MarkChapters(menus->mg_vg);
1944 setattr(menus->mg_vg, VTYPE_VMGM);
1945 fprintf(stderr, "\n");
1946 FixVobus(fbuf, menus->mg_vg, &ws, VTYPE_VMGM);
1947 }
1948 else
1949 /* unconditional because there will always be at least one PGC,
1950 namely the FPC (explicit or default) */
1951 {
1952 set_video_format_attr(menus->mg_vg, VTYPE_VMGM); /* for the sake of buildtimeeven */
1953 } /*if*/
1954 /* (re)generate VMG IFO */
1955 snprintf(fbuf, sizeof fbuf, "%s/VIDEO_TS.IFO", vtsdir);
1956 TocGen(&ws, fpc, fbuf);
1957 snprintf(fbuf, sizeof fbuf, "%s/VIDEO_TS.BUP", vtsdir); /* same thing again, backup copy */
1958 TocGen(&ws, fpc, fbuf);
1959 for (i = 0; i < ts.numvts; i++)
1960 if (ts.vts[i].numchapters)
1961 free(ts.vts[i].numchapters);
1962 free(vtsdir);
1963 } /*dvdauthor_vmgm_gen*/
1964
dvdauthor_vts_gen(struct menugroup * menus,struct pgcgroup * titles,const char * fbase)1965 void dvdauthor_vts_gen(struct menugroup *menus, struct pgcgroup *titles, const char *fbase)
1966 /* generates a VTS (titleset). */
1967 {
1968 int vtsnum, i;
1969 static char realfbase[1000];
1970 struct workset ws;
1971
1972 fprintf(stderr, "INFO: dvdauthor creating VTS\n");
1973 initdir(fbase);
1974 ws.titlesets = 0;
1975 ws.menus = menus;
1976 ws.titles = titles;
1977 jp_force_menu(menus, VTYPE_VTSM);
1978 for (i = 0; i < menus->numgroups; i++)
1979 {
1980 validatesummary(menus->groups[i].pg);
1981 pgcgroup_createvobs(menus->groups[i].pg, menus->mg_vg);
1982 forceaddentry(menus->groups[i].pg, 0x80); /* entry=ptt? */
1983 checkaddentry(menus->groups[i].pg, 0x08); /* entry=root */
1984 } /*for*/
1985 validatesummary(titles);
1986 pgcgroup_createvobs(titles, titles->pg_vg);
1987 if (titles->numpgcs == 0)
1988 {
1989 fprintf(stderr, "ERR: no titles defined\n");
1990 exit(1);
1991 } /*if*/
1992 vtsnum = getvtsnum(fbase);
1993 if (fbase)
1994 {
1995 snprintf(realfbase, sizeof realfbase, "%s/VIDEO_TS/VTS_%02d", fbase, vtsnum);
1996 fbase = realfbase;
1997 } /*if*/
1998 if (menus->mg_vg->numvobs != 0)
1999 {
2000 FindVobus(fbase, menus->mg_vg, VTYPE_VTSM);
2001 MarkChapters(menus->mg_vg);
2002 setattr(menus->mg_vg, VTYPE_VTSM);
2003 }
2004 else if (menus->mg_vg->numallpgcs != 0)
2005 {
2006 set_video_format_attr(menus->mg_vg, VTYPE_VTSM); /* for the sake of buildtimeeven */
2007 } /*if*/
2008 FindVobus(fbase, titles->pg_vg, VTYPE_VTS);
2009 MarkChapters(titles->pg_vg);
2010 setattr(titles->pg_vg, VTYPE_VTS);
2011 if (!menus->mg_vg->numvobs) // for undefined menus, we'll just copy the video type of the title
2012 {
2013 menus->mg_vg->vd = titles->pg_vg->vd;
2014 } /*if*/
2015 fprintf(stderr, "\n");
2016 WriteIFOs(fbase, &ws);
2017 if (menus->mg_vg->numvobs)
2018 FixVobus(fbase, menus->mg_vg, &ws, VTYPE_VTSM);
2019 FixVobus(fbase, titles->pg_vg, &ws, VTYPE_VTS);
2020 } /*dvdauthor_vts_gen*/
2021