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