1 /*
2     dvdauthor -- generation of IFO and BUP files
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 "config.h"
24 #include "compat.h"
25 #include <errno.h>
26 #include <assert.h>
27 
28 #include "dvdauthor.h"
29 #include "da-internal.h"
30 
31 static unsigned char *
32     bigbuf = 0;
33 static size_t
34     bigbufsize = 0;
35 
36 #define MIN_IFO_SECTORS 16
37   /* need to ensure that .IFO and .BUP files end up in different ECC blocks */
38 
buf_init()39 static void buf_init()
40   /* ensures there's no leftover junk in bigbuf. */
41   {
42     free(bigbuf);
43     bigbuf = 0;
44     bigbufsize = 0;
45   } /*buf_init*/
46 
buf_need(size_t sizeneeded)47 static void buf_need(size_t sizeneeded)
48   /* ensures that bigbuf is at least sizeneeded bytes in size. */
49   {
50     if (sizeneeded > bigbufsize)
51       {
52         const size_t newbufsize = (sizeneeded + 2047) / 2048 * 2048; /* allocate next whole sector */
53         // fprintf(stderr, "INFO: need_buf: bigbufsize now %ld sectors.\n", newbufsize / 2048);
54         bigbuf = realloc(bigbuf, newbufsize);
55         if (bigbuf == 0)
56           {
57             fprintf(stderr, "ERR:  buf_need: out of memory\n");
58             exit(1);
59           } /*if*/
60         memset(bigbuf + bigbufsize, 0, newbufsize - bigbufsize); /* zero added memory */
61         bigbufsize = newbufsize;
62       } /*if*/
63   } /*buf_need*/
64 
buf_write1(size_t o,unsigned char b)65 static void buf_write1(size_t o, unsigned char b)
66   /* puts a byte into buf at offset o. */
67   {
68     buf_need(o + 1);
69     bigbuf[o] = b;
70   }/*buf_write1*/
71 
buf_write2(size_t o,unsigned short w)72 static void buf_write2(size_t o, unsigned short w)
73   /* puts a big-endian word into buf at offset o. */
74   {
75     buf_need(o + 2);
76     bigbuf[o] = w >> 8 & 255;
77     bigbuf[o + 1] = w & 255;
78   } /*buf_write2*/
79 
buf_write4(size_t o,unsigned int l)80 static void buf_write4(size_t o, unsigned int l)
81   /* puts a big-endian longword into buf at offset o. */
82   {
83     buf_need(o + 4);
84     bigbuf[o] = l >> 24 & 255;
85     bigbuf[o + 1] = l >> 16 & 255;
86     bigbuf[o + 2] = l >> 8 & 255;
87     bigbuf[o + 3] = l & 255;
88   } /*buf_write4*/
89 
buf_write8b(size_t o,unsigned char b0,unsigned char b1,unsigned char b2,unsigned char b3,unsigned char b4,unsigned char b5,unsigned char b6,unsigned char b7)90 static void buf_write8b
91   (
92     size_t o,
93     unsigned char b0,
94     unsigned char b1,
95     unsigned char b2,
96     unsigned char b3,
97     unsigned char b4,
98     unsigned char b5,
99     unsigned char b6,
100     unsigned char b7
101   )
102   /* writes 8 bytes into buf at offset o. */
103   {
104     buf_need(o + 8);
105     bigbuf[o] = b0;
106     bigbuf[o + 1] = b1;
107     bigbuf[o + 2] = b2;
108     bigbuf[o + 3] = b3;
109     bigbuf[o + 4] = b4;
110     bigbuf[o + 5] = b5;
111     bigbuf[o + 6] = b6;
112     bigbuf[o + 7] = b7;
113   } /*buf_write8b*/
114 
nfwrite(const void * ptr,size_t len,FILE * h)115 static void nfwrite(const void *ptr, size_t len, FILE *h)
116   /* writes to h, or turns into a noop if h is null. */
117   {
118     if (h)
119       {
120         errno = 0; /* where is this being set? */
121         (void)fwrite(ptr, len, 1, h);
122         if (errno != 0)
123           {
124             fprintf
125               (
126                 stderr,
127                 "\nERR:  Error %d -- %s -- writing output IFO\n",
128                 errno,
129                 strerror(errno)
130               );
131             exit(1);
132           } /*if*/
133       } /*if*/
134   } /*nfwrite*/
135 
nfpad(size_t len,FILE * h)136 static void nfpad(size_t len, FILE *h)
137   /* writes len bytes of padding to h, or turns into a noop if h is null. */
138   {
139     static unsigned char buf[2048];
140     static size_t himark = 0;
141     if (h != NULL)
142       {
143         while (len != 0)
144           {
145             size_t uselen = sizeof buf;
146             if (uselen > len)
147               {
148                 uselen = len;
149               } /*if*/
150             if (uselen > himark)
151               {
152                 memset(buf + himark, 0, uselen - himark);
153                 himark = uselen;
154               } /*if*/
155             nfwrite(buf, uselen, h);
156             len -= uselen;
157           } /*while*/
158       } /*if*/
159   } /*nfpad*/
160 
globalfindvobu(const struct pgc * ch,int pts)161 static const struct vobuinfo *globalfindvobu(const struct pgc *ch, int pts)
162   /* finds the VOBU spanning the specified time. */
163   {
164     int s, c, ci;
165     for (s = 0; s < ch->numsources; s++)
166       {
167         const struct source * const thissource = ch->sources[s];
168         for (c = 0; c < thissource->numcells; c++)
169           {
170             const struct cell * const thiscell = &thissource->cells[c];
171             int span = 0;
172             const int fv = findcellvobu(thissource->vob, thiscell->scellid);
173             if (pts < 0)
174                 return
175                     &thissource->vob->vobu[fv];
176             for (ci = thiscell->scellid; ci < thiscell->ecellid; ci++)
177                 span += getcellpts(thissource->vob, ci);
178             if (pts < span)
179               {
180               /* desired time lies within timespan of this cell */
181                 const int r = findvobu
182                   (
183                     /*va =*/ thissource->vob,
184                     /*pts =*/ pts + thissource->vob->vobu[fv].sectpts[0],
185                       /* offset from start time */
186                     /*l =*/ fv,
187                     /*h =*/ thissource->vob->numvobus-1
188                   );
189                 return
190                     &thissource->vob->vobu[r];
191               } /*if*/
192             pts -= span; /* desired time lies beyond this cell */
193           } /*for*/
194       } /*for*/
195     // return last vob
196     // if (ch->numsources) {
197     // struct vob *s = ch->sources[ch->numsources - 1];
198     // return &s->vobu[s->numvobus - 1];
199     // }
200     return
201         0;
202   } /*globalfindvobu*/
203 
getvoblen(const struct vobgroup * va)204 static int getvoblen(const struct vobgroup *va)
205   /* returns the length in sectors of the VOB group. */
206   {
207     int i;
208     for (i = va->numvobs - 1; i >= 0; i--)
209         if (va->vobs[i]->numvobus)
210             return
211                 va->vobs[i]->vobu[va->vobs[i]->numvobus - 1].lastsector + 1;
212     return
213         0;
214   } /*getvoblen*/
215 
pts_seconds_to_ticks(const struct vobgroup * va,int nsec)216 static unsigned int pts_seconds_to_ticks(const struct vobgroup *va, int nsec)
217   /* converts nsec seconds to clock ticks adjusted for the video frame rate. */
218   {
219     return nsec * getratedenom(va);
220   } /*pts_seconds_to_ticks*/
221 
pts_ticks_to_seconds(const struct vobgroup * va,int pts)222 static unsigned int pts_ticks_to_seconds(const struct vobgroup *va, int pts)
223   /* converts pts frame-rate-adjusted clock ticks to truncated seconds. */
224   {
225     return pts / getratedenom(va);
226   } /*pts_ticks_to_seconds*/
227 
get_pgc_duration_seconds(const struct pgcgroup * va,int c)228 static int get_pgc_duration_seconds(const struct pgcgroup *va, int c)
229   /* returns the duration in truncated seconds of the PGC with the specified index. */
230   {
231     // we subtract 1 because there is a bug if getptsspan() returns
232     // an exact multiple of 90090*units; if so, then the last entry of the
233     // TMAPT table cannot be properly computed, because that entry will have
234     // fallen off the end of the VOBU table
235     return pts_ticks_to_seconds(va->pg_vg, getptsspan(va->pgcs[c]) - 1);
236   } /*get_pgc_duration_seconds*/
237 
secunit(int ns)238 static int secunit(int ns)
239   /* returns ns / 2040 rounded up. This is the duration in seconds of each VTS_TMAP entry
240     for a PGC with total duration ns to ensure the number of entries never exceeds 2048. */
241   {
242     const int maxunits = 2040; /* ensure nr entries don't exceed 2040, just to be safe I guess */
243     if (!ns)
244         return 1; /* minimum unit of 1 second */
245     return
246         (ns + maxunits - 1) / maxunits;
247   } /*secunit*/
248 
tmapt_block_size(const struct pgcgroup * va,int pgc)249 static int tmapt_block_size(const struct pgcgroup *va, int pgc)
250   /* computes the size of the VTS_TMAP entries for one PGC. */
251   {
252     int v = get_pgc_duration_seconds(va, pgc);
253       /* start by assuming one VOBU per second (VOBUs shouldn't be longer than one second) */
254     v = v / secunit(v); /* if that would be too many, then adjust to one per n seconds */
255     return
256         v * 4 + 4; /* 4-byte header plus 4 bytes per VOBU */
257   } /*tmapt_block_size*/
258 
sizeTMAPT(const struct pgcgroup * va)259 static int sizeTMAPT(const struct pgcgroup *va)
260   /* computes the total size of all the VTS_TMAP arrays for this PGC group. */
261   {
262     int s = 0, i;
263     for (i = 0; i < va->numpgcs; i++)
264         s += tmapt_block_size(va, i);
265     return
266         s + va->numpgcs * 4 + 8;
267   } /*sizeTMAPT*/
268 
numsectTMAPT(const struct pgcgroup * va)269 static int numsectTMAPT(const struct pgcgroup *va)
270   /* computes the total number of sectors to hold all the VTS_TMAP arrays for this PGC group. */
271   {
272     return
273         (sizeTMAPT(va) + 2047) / 2048;
274   } /*numsectTMAPT*/
275 
CreateTMAPT(FILE * h,const struct pgcgroup * va)276 static void CreateTMAPT(FILE *h, const struct pgcgroup *va)
277 /* creates the VTS_TMAPTI structure which contains the time maps for each PGC. */
278 {
279     int pgcindex, mapblock;
280     unsigned char buf[8];
281 
282     write2(buf, va->numpgcs); /* nr program chains, low word */
283     write2(buf + 2, 0); /* nr program chains, high word */
284     write4(buf + 4, sizeTMAPT(va) - 1); /* end address (last byte of last VTS_TMAP) */
285     nfwrite(buf, 8, h);
286 
287     mapblock = 8 + 4 * va->numpgcs;
288     for (pgcindex = 0; pgcindex < va->numpgcs; pgcindex++)
289       {
290         write4(buf, mapblock); /* offset to VTS_TMAP[pgcindex + 1] */
291         nfwrite(buf, 4, h);
292         mapblock += tmapt_block_size(va, pgcindex);
293       } /*for*/
294 
295     for (pgcindex = 0; pgcindex < va->numpgcs; pgcindex++)
296       {
297       /* fill in each VTS_TMAP */
298         int numtmapt = get_pgc_duration_seconds(va, pgcindex), ptsbase, j;
299         const int units = secunit(numtmapt);
300         const struct pgc * const thispgc = va->pgcs[pgcindex];
301 
302         numtmapt /= units;
303         buf[0] = units; /* time units, seconds */
304         buf[1] = 0; /* unused */
305         write2(buf + 2, numtmapt); /* nr entries in map, [0 .. 2048] */
306         nfwrite(buf, 4, h); /* write VTS_TMAP header */
307         if (numtmapt > 0)
308           {
309           /* write VTS_TMAP entries */
310             const struct vobuinfo *vobu1;
311             // I don't know why I ever did this
312             // ptsbase = -getframepts(va->pg_vg);
313             ptsbase = 0; // this matches Bullitt
314             vobu1 = globalfindvobu(thispgc, ptsbase + pts_seconds_to_ticks(va->pg_vg, units));
315             for (j = 0; j < numtmapt; j++)
316               {
317                 const struct vobuinfo * const vobu2 = globalfindvobu
318                   (
319                     thispgc,
320                     ptsbase + pts_seconds_to_ticks(va->pg_vg, (j + 2) * units)
321                   );
322                 write4(buf, vobu1->sector);
323                 if (!vobu2 || vobu1->vobcellid != vobu2->vobcellid)
324                     buf[0] |= 0x80; /* next time entry will be for a different cell */
325                 nfwrite(buf, 4, h);
326                 vobu1 = vobu2;
327               } /*for*/
328           } /*if*/
329       } /*for*/
330 
331     pgcindex = (-sizeTMAPT(va)) & 2047;
332     if (pgcindex)
333       {
334       /* clear out unused part of last sector */
335         memset(buf, 0, 8);
336         while(pgcindex >= 8)
337           {
338             nfwrite(buf, 8, h);
339             pgcindex -= 8;
340           } /*while*/
341         if (pgcindex) /* shouldn't occur? */
342             nfwrite(buf, pgcindex, h);
343       } /*if*/
344 }
345 
numsectVOBUAD(const struct vobgroup * va)346 static int numsectVOBUAD(const struct vobgroup *va)
347 /* returns the number of sectors a VOBU_ADMAP will take up. */
348 {
349     int nv = 0, i;
350     for (i = 0; i < va->numvobs; i++)
351         nv += va->vobs[i]->numvobus;
352     return (4+nv*4+2047)/2048;
353 }
354 
CreateCellAddressTable(FILE * h,const struct vobgroup * va)355 static int CreateCellAddressTable(FILE *h, const struct vobgroup *va)
356   /* outputs a VMGM_C_ADT, VTSM_C_ADT or VTS_C_ADT structure containing pointers to all cells. */
357   {
358     int i, p, k;
359     buf_init();
360     p = 8;
361     for (k = 0; k < va->numvobs; k++)
362       {
363         const struct vob * const thisvob = va->vobs[k];
364         for (i = 0; i < thisvob->numvobus; i++)
365           {
366             if (!i || thisvob->vobu[i].vobcellid != thisvob->vobu[i - 1].vobcellid)
367               { /* starting a new cell */
368                 if (i)
369                   {
370                     buf_write4(p + 8, thisvob->vobu[i - 1].lastsector);
371                       /* ending sector within VOB in previous entry */
372                     p += 12;
373                   } /*if*/
374                 buf_write2(p, thisvob->vobu[i].vobcellid >> 8); /* VOBidn */
375                 buf_write1(p + 2, thisvob->vobu[i].vobcellid); /* CELLidn */
376                 buf_write4(p + 4, thisvob->vobu[i].sector); /* starting sector within VOB */
377               } /*if*/
378           } /*for*/
379         buf_write4(p + 8, thisvob->vobu[i - 1].lastsector);
380           /* ending sector within VOB in last entry */
381         p += 12;
382       } /*for*/
383     buf_write4(4, p - 1); /* end address (last byte of last entry) */
384     // first 2 bytes of C_ADT contains number of vobs
385     buf_write2(0, va->numvobs);
386     p = (p + 2047) & (-2048); /* round up to whole sectors */
387     nfwrite(bigbuf, p, h);
388     return p / 2048; /* nr sectors written */
389   } /*CreateCellAddressTable*/
390 
CreateVOBUAD(FILE * h,const struct vobgroup * va)391 static void CreateVOBUAD(FILE *h, const struct vobgroup *va)
392 /* outputs a VOBU_ADMAP structure containing pointers to all VOBUs. */
393   {
394     int i, j, nv;
395     unsigned char buf[16];
396     nv = 0;
397     for (i = 0; i < va->numvobs; i++)
398         nv += va->vobs[i]->numvobus;
399     write4(buf, nv * 4 + 3); /* end address (last byte of last entry) */
400     nfwrite(buf, 4, h);
401     for (j = 0; j < va->numvobs; j++)
402       {
403         const struct vob * const thisvob = va->vobs[j];
404         for (i = 0; i < thisvob->numvobus; i++)
405           {
406             write4(buf, thisvob->vobu[i].sector); /* starting sector of VOBU within VOB */
407             nfwrite(buf, 4, h);
408           } /*for*/
409       } /*for*/
410     i = (-(4 + nv * 4)) & 2047;
411     if (i)
412       {
413       /* zero out unused part of last whole sector */
414         memset(buf, 0, 16);
415         while (i >= 16)
416           {
417             nfwrite(buf, 16, h);
418             i -= 16;
419           } /*while*/
420         if (i)
421             nfwrite(buf, i, h);
422       } /*if*/
423   } /*CreateVOBUAD*/
424 
Create_PTT_SRPT(FILE * h,const struct pgcgroup * t)425 static int Create_PTT_SRPT(FILE *h, const struct pgcgroup *t)
426   /* creates the VTS_PTT_SRPT and VTS_PTT tables for each title. */
427   {
428     int i, j, p;
429     buf_init();
430     buf_write2(0, t->numpgcs); // # of titles
431     p = 8 + t->numpgcs * 4; /* start generating VTS_PTT entries here */
432     assert(p <= 2048);
433       // need to make sure all the pgc pointers fit in the first sector because of
434       // dvdauthor.c:ScanIfo
435     for (j = 0; j < t->numpgcs; j++)
436       {
437         const struct pgc * const pgc = t->pgcs[j];
438         int pgm = 1, k;
439         buf_write4(8 + j * 4, p); /* offset to VTS_PTT for title */
440         for (i = 0; i < pgc->numsources; i++) /* generate the associated VTS_PTT entries */
441             for (k = 0;  k < pgc->sources[i]->numcells; k++)
442               {
443                 const struct cell * const thiscell = &pgc->sources[i]->cells[k];
444                 if (thiscell->scellid != thiscell->ecellid)
445                     switch (thiscell->ischapter)
446                       {
447                     case CELL_CHAPTER_PROGRAM:
448                         buf_write1(1 + p, j + 1); /* PGCN low byte */
449                         buf_write1(3 + p, pgm); /* PGN low byte */
450                         p += 4;
451                   /* fallthru */
452                     case CELL_PROGRAM:
453                         pgm++; /* keep right count for next chapter */
454                     break;
455                     case CELL_NEITHER:
456                       /* fine */
457                     break;
458                       } /*switch*/
459               } /*for; for*/
460       } /*for*/
461     buf_write4(4, p - 1); /* end address (last byte of last VTS_PTT) */
462     p = (p + 2047) & (-2048); /* round up to next whole sector */
463     nfwrite(bigbuf, p, h); /* write it all out */
464     return p / 2048; /* nr sectors generated */
465   } /*Create_PTT_SRPT*/
466 
Create_TT_SRPT(FILE * h,const struct toc_summary * ts,int vtsstart)467 static int Create_TT_SRPT
468   (
469     FILE *h,
470     const struct toc_summary *ts,
471     int vtsstart /* starting sector for VTS */
472   )
473   /* creates a TT_SRPT structure containing pointers to all the titles on the disc. */
474   {
475     int i, j, k, p, tn;
476     buf_init();
477     j = vtsstart;
478     tn = 0;
479     p = 8; /* offset to first entry */
480     for (i = 0; i < ts->numvts; i++)
481       {
482         for (k = 0; k < ts->vts[i].numtitles; k++)
483           {
484             buf_write1(0 + p, 0x3c);
485               /* title type = one sequential PGC, jump/link/call may be found in all places,
486                 PTT & time play/search uops not inhibited */
487             buf_write1(1 + p, 0x1); /* number of angles always 1 for now */
488             buf_write2(2 + p, ts->vts[i].numchapters[k]); /* number of chapters (PTTs) */
489             buf_write1(6 + p, i + 1); /* video titleset number, VTSN */
490             buf_write1(7 + p, k + 1); /* title nr within VTS, VTS_TTN */
491             buf_write4(8 + p, j); // start sector for VTS
492             tn++;
493             p += 12; /* offset to next entry */
494           } /*for*/
495         j += ts->vts[i].numsectors;
496       } /*for*/
497     buf_write2(0, tn); // # of titles
498     buf_write4(4, p - 1); /* end address (last byte of last entry) */
499     p = (p + 2047) & (-2048); /* round up to next whole sector */
500     nfwrite(bigbuf, p, h);
501     return p / 2048; /* nr sectors generated */
502   } /*Create_TT_SRPT*/
503 
BuildAVInfo(unsigned char * buf,const struct vobgroup * va)504   static void BuildAVInfo(unsigned char *buf, const struct vobgroup *va)
505   /* builds the part of the IFO structure from offset 0x100 (VMGM, VTSM) and 0x200 (VTS) onwards,
506     containing the video, audio and subpicture stream attributes. Note these attributes
507     don't include any stream IDs; those are specified per-PGC. */
508   {
509     int i;
510     static const int widescreen_bits[4] =
511         {
512             0, /* VW_NONE */
513             0x100, /* VW_NOLETTERBOX */
514             0x200, /* VW_NOPANSCAN */
515             2 /* VW_CROP */
516         };
517     write2
518       (
519         buf,
520             (va->vd.vmpeg == 2 ? 0x4000 : 0) /* coding mode: MPEG-1 or MPEG-2 */
521         |
522             widescreen_bits[va->vd.vwidescreen]
523               /* whether to allow automatic pan-scan, automatic letterbox, do cropping */
524         |
525             (va->vd.vformat == VF_PAL ? 0x1000 : 0) /* NTSC or PAL */
526         |
527             (va->vd.vaspect == VA_16x9 ? 0xc00 : 0x300)
528               // if 16:9, set aspect flag; if 4:3 set noletterbox/nopanscan
529         |
530             ((va->vd.vcaption & 1) ? 0x80 : 0)
531               /* caption=field1 (line-21 closed-captioning, NTSC only) */
532         |
533             ((va->vd.vcaption & 2) ? 0x40 : 0)
534               /* caption=field2 (line-21 closed-captioning, NTSC only) */
535         |
536             ((va->vd.vres - 1) << 3) /* resolution code */
537       );
538         /* bit rate always VBR, letterbox-cropped unset, PAL film flag unset for now */
539     buf[3] = va->numaudiotracks; /* nr audio streams, low byte */
540     for (i = 0; i < va->numaudiotracks; i++)
541       { /* fill in menu/title audio attributes */
542         buf[4 + i * 8] = (va->ad[i].aformat - 1) << 6; /* audio coding mode */
543         if (va->ad[i].alangpresent == AL_LANG) /* for title audio, not menu audio */
544           {
545             buf[4 + i * 8] |= 4; /* language type = as per language code */
546             memcpy(buf + 6 + i * 8, va->ad[i].lang, 2); /* language code */
547           } /*if*/
548       /* multichannel extension not supported for now */
549         if (va->ad[i].adolby == AD_SURROUND) /* for title audio, not menu audio */
550           {
551             buf[4 + i * 8] |= 2; /* application mode = surround */
552             buf[11 + i * 8] = 8; /* suitable for Dolby surround decoding */
553           } /*if*/
554       /* karaoke options not supported for now */
555         buf[5 + i * 8] =
556                 ((va->ad[i].aquant - 1) << 6) /* quantization/DRC */
557             |
558                 ((va->ad[i].asample - 1) << 4) /* sample rate */
559             |
560                 (va->ad[i].achannels - 1); /* nr channels - 1 */
561 
562         buf[9 + i * 8] = va->ad[i].acontent; /* audio code extension for title audio, not menu audio */
563       } /*for*/
564     buf[0x55] = va->numsubpicturetracks; /* nr subpicture streams, low byte */
565     for (i = 0; i < va->numsubpicturetracks; i++)
566       {
567       /* coding mode always RLE */
568         if (va->sp[i].slangpresent == AL_LANG) /* for title subpicture, not menu subpicture */
569           {
570             buf[0x56 + i * 6] = 1; /* language type = as per language code */
571             memcpy(buf + 0x58 + i * 6, va->sp[i].lang, 2); /* language code */
572           } /*if*/
573         buf[0x56 + i * 6 + 5] = va->sp[i].scontent;
574           /* title code extension (title subpicture only) */
575       } /*for*/
576   } /*BuildAVInfo*/
577 
needmenus(const struct menugroup * mg)578 static bool needmenus(const struct menugroup *mg)
579 /* do I actually have any menu definitions in mg. */
580 {
581     if (!mg ) return false;
582     if( !mg->numgroups ) return false;
583     if( !mg->groups[0].pg->numpgcs ) return false; /* fixme: what about checking rest of groups? */
584     return true;
585 }
586 
WriteIFO(FILE * h,const struct workset * ws)587 static void WriteIFO(FILE *h, const struct workset *ws)
588   /* writes the IFO for a VTSM. */
589   {
590     static unsigned char buf[2048];
591     int nextsector;
592     const bool forcemenus = needmenus(ws->menus);
593     size_t ifo_pad = 0;
594 
595     // sect 0: VTS toplevel
596     memset(buf, 0, 2048);
597     memcpy(buf, "DVDVIDEO-VTS", 12);
598     buf[33] = 0x11; /* version number */
599     write4(buf + 128, 0x7ff);
600     nextsector = 1;
601 
602     write4(buf + 0xC8, nextsector); // VTS_PTT_SRPT
603     nextsector += Create_PTT_SRPT(0, ws->titles);
604 
605     write4(buf + 0xCC, nextsector); // VTS_PGCI
606     nextsector += CreatePGC(0, ws, VTYPE_VTS);
607 
608     if (jumppad || forcemenus)
609       {
610         write4(buf + 0xD0,nextsector); // VTSM_PGCI
611         nextsector += CreatePGC(0, ws, VTYPE_VTSM);
612       } /*if*/
613 
614     write4(buf + 0xD4, nextsector); // VTS_TMAPT
615     nextsector += numsectTMAPT(ws->titles);
616 
617     if (jumppad || forcemenus)
618       {
619         write4(buf + 0xD8, nextsector); // VTSM_C_ADT
620         nextsector += CreateCellAddressTable(0, ws->menus->mg_vg);
621 
622         write4(buf + 0xDC, nextsector); // VTSM_VOBU_ADMAP
623         nextsector += numsectVOBUAD(ws->menus->mg_vg);
624       } /*if*/
625 
626     write4(buf + 0xE0, nextsector); // VTS_C_ADT
627     nextsector += CreateCellAddressTable(0, ws->titles->pg_vg);
628 
629     write4(buf + 0xE4, nextsector); // VTS_VOBU_ADMAP
630     nextsector += numsectVOBUAD(ws->titles->pg_vg);
631 
632     write4(buf + 28, nextsector - 1); /* last sector of IFO */
633     if (jumppad || forcemenus)
634       {
635         write4(buf + 0xC0, nextsector); /* start sector of menu VOB */
636         nextsector += getvoblen(ws->menus->mg_vg);
637       } /*if*/
638     write4(buf + 0xC4, nextsector); /* start sector of title VOB */
639     if (ws->titles->numpgcs)
640         nextsector += getvoblen(ws->titles->pg_vg);
641     nextsector += read4(buf + 28); /* offset by last sector of IFO */
642     if (nextsector < MIN_IFO_SECTORS - 1)
643       {
644         ifo_pad = (MIN_IFO_SECTORS - 1 - nextsector) * 2048;
645         nextsector = MIN_IFO_SECTORS - 1;
646       } /*if*/
647     write4(buf + 12, nextsector); /* gives last sector of title set (last sector of BUP) */
648 
649     if (jumppad || forcemenus)
650         BuildAVInfo(buf + 256, ws->menus->mg_vg);
651     BuildAVInfo(buf + 512, ws->titles->pg_vg);
652     nfwrite(buf, 2048, h);
653 
654     // sect 1: VTS_PTT_SRPT
655     Create_PTT_SRPT(h, ws->titles);
656 
657     // sect 2: VTS_PGCI
658     CreatePGC(h, ws, VTYPE_VTS);
659 
660     if( jumppad || forcemenus )
661         CreatePGC(h, ws, VTYPE_VTSM);
662 
663     // sect 3: ??? VTS_TMAPT
664     CreateTMAPT(h, ws->titles);
665 
666     if (jumppad || forcemenus)
667       {
668         CreateCellAddressTable(h, ws->menus->mg_vg);
669         CreateVOBUAD(h, ws->menus->mg_vg);
670       } /*if*/
671     CreateCellAddressTable(h, ws->titles->pg_vg);
672     CreateVOBUAD(h, ws->titles->pg_vg);
673   /* nfpad(ifo_pad, h); */ /* unneeded, genisoimage will fix it for me */
674   } /*WriteIFO*/
675 
WriteIFOs(const char * fbase,const struct workset * ws)676 void WriteIFOs(const char *fbase, const struct workset *ws)
677 /* writes out a .IFO and corresponding .BUP file for a VTSM. */
678   {
679     FILE *h;
680     static char buf[1000];
681     bool backup;
682 
683     errno = 0;
684     if (fbase)
685       {
686         backup = false;
687         for (;;)
688           {
689             snprintf(buf, sizeof buf, "%s_0.%s", fbase, backup ? "BUP" : "IFO");
690             h = fopen(buf, "wb");
691             if (!h)
692               {
693                 fprintf
694                   (
695                     stderr,
696                     "\nERR:  Error %d -- %s -- creating %s\n",
697                     errno,
698                     strerror(errno),
699                     buf
700                   );
701                 exit(1);
702               } /*if*/
703             WriteIFO(h, ws);
704             fflush(h);
705             if (errno != 0)
706               {
707                 fprintf
708                   (
709                     stderr,
710                     "\nERR:  Error %d -- %s -- flushing %s\n",
711                     errno,
712                     strerror(errno),
713                     buf
714                   );
715                 exit(1);
716               } /*if*/
717             fclose(h);
718             if (backup)
719                 break;
720             backup = true;
721           } /*for*/
722       }
723     else
724       /* dummy write */
725         WriteIFO(0, ws);
726   } /*WriteIFOs*/
727 
TocGen(const struct workset * ws,const struct pgc * fpc,const char * fname)728 void TocGen(const struct workset *ws, const struct pgc *fpc, const char *fname)
729   /* writes the IFO for a VMGM. */
730   {
731     static unsigned char buf[2048];
732     int nextsector, offset, i, j, vtsstart;
733     const bool forcemenus = needmenus(ws->menus);
734     FILE *h;
735     size_t ifo_pad = 0;
736 
737     h = fopen(fname, "wb");
738 
739     memset(buf, 0, 2048);
740     memcpy(buf, "DVDVIDEO-VMG", 12);
741     buf[0x21] = 0x11; /* version number */
742     buf[0x27] = 1; /* number of volumes */
743     buf[0x29] = 1; /* volume number */
744     buf[0x2a] = 1; /* side ID */
745     write2(buf + 0x3e, ws->titlesets->numvts); /* number of title sets */
746     strncpy((char *)(buf + 0x40), provider_str, PROVIDER_SIZE - 1); /* provider ID */
747     buf[0x86] = 4; /* start address of FP_PGC = 0x400 */
748     nextsector = 1;
749 
750     write4(buf + 0xc4, nextsector); /* sector pointer to TT_SRPT (table of titles) */
751     nextsector += Create_TT_SRPT(0, ws->titlesets, 0);
752       /* just to figure out how many sectors will be needed */
753 
754     if (jumppad || forcemenus)
755       {
756         write4(buf + 0xc8, nextsector); /* sector pointer to VMGM_PGCI_UT (menu PGC table) */
757         nextsector += CreatePGC(0, ws, VTYPE_VMGM);
758       } /*if*/
759 
760     write4(buf + 0xd0, nextsector);
761       /* sector pointer to VMG_VTS_ATRT (copies of VTS audio/subpicture attrs) */
762       /* I will output it immediately following IFO header */
763     nextsector += (8 + ws->titlesets->numvts * 0x30c + 2047) / 2048;
764       /* round up size of VMG_VTS_ATRT to whole sectors */
765 
766     if (jumppad || forcemenus)
767       {
768         write4(buf + 0xd8, nextsector);
769           /* sector pointer to VMGM_C_ADT (menu cell address table) */
770           /* I make it follow VMG_VTS_ATRT */
771         nextsector += CreateCellAddressTable(0, ws->menus->mg_vg); /* how much room it will need */
772 
773         write4(buf + 0xdc, nextsector);
774           /* sector pointer to VMGM_VOBU_ADMAP (menu VOBU address map) */
775         nextsector += numsectVOBUAD(ws->menus->mg_vg);
776       } /*if*/
777 
778     write4(buf + 0x1c, nextsector - 1); /* last sector of IFO */
779     vtsstart = nextsector * 2; /* size of two copies of everything above including BUP */
780     if (jumppad || forcemenus)
781       {
782         write4(buf + 0xc0, nextsector); /* start sector of menu VOB */
783         vtsstart += getvoblen(ws->menus->mg_vg);
784       } /*if*/
785     if (vtsstart < 2 * MIN_IFO_SECTORS)
786       {
787         ifo_pad = (2 * MIN_IFO_SECTORS - vtsstart) * 2048 / 2;
788         vtsstart = 2 * MIN_IFO_SECTORS;
789       } /*if*/
790     write4(buf + 0xc, vtsstart - 1); /* last sector of VMG set (last sector of BUP) */
791 
792     if (forcemenus)
793         BuildAVInfo(buf + 256, ws->menus->mg_vg);
794 
795   /* create FPC at 0x400 as promised */
796     buf[0x407] = (getratedenom(ws->menus->mg_vg) == 90090 ? 3 : 1) << 6;
797       // only set frame rate XXX: should check titlesets if there is no VMGM menu
798     buf[0x4e5] = 0xec; /* offset to command table, low byte */
799     offset = 0x4f4; /* commands start here, after 8-byte header of command table */
800     if (fpc)
801       {
802         unsigned char *pi;
803         if (fpc->posti || fpc->numsources || fpc->numbuttons || fpc->entries)
804           {
805             fprintf(stderr,"ERR:  FPC can ONLY contain prei commands, nothing else\n");
806             exit(1);
807           } /*if*/
808         if (ws->menus && ws->menus->numgroups)
809             pi = vm_compile(buf + offset, buf + offset, ws, ws->menus->groups[0].pg, 0, fpc->prei, 2);
810               // XXX: just use the first pgcgroup as a reference
811         else
812             pi = vm_compile(buf + offset, buf + offset, ws, 0, 0, fpc->prei, 2);
813         if (!pi)
814           {
815             fprintf(stderr,"ERR:  in FPC\n");
816             exit(1);
817           } /*if*/
818         offset = (pi - buf - offset) / 8; /* number of instructions */
819         assert(offset <= 128);
820         buf[0x4ed] = offset; /* number of pre commands, low byte */
821       }
822     else
823       {
824       /* generate default FPC */
825         if (forcemenus)
826           {
827             buf[offset + 0] = 0x30; // jump to VMGM 1
828             buf[offset + 1] = 0x06;
829             buf[offset + 2] = 0x00;
830             buf[offset + 3] = 0x00;
831             buf[offset + 4] = 0x00;
832             buf[offset + 5] = 0x42;
833             buf[offset + 6] = 0x00;
834             buf[offset + 7] = 0x00;
835           }
836         else if (ws->titlesets->numvts && ws->titlesets->vts[0].hasmenu)
837           {
838             buf[offset + 0] = 0x30; // jump to VTSM vts=1, ttn=1, menu=1
839             buf[offset + 1] = 0x06;
840             buf[offset + 2] = 0x00;
841             buf[offset + 3] = 0x01;
842             buf[offset + 4] = 0x01;
843             buf[offset + 5] = 0x83;
844             buf[offset + 6] = 0x00;
845             buf[offset + 7] = 0x00;
846           }
847         else
848           {
849             buf[offset + 0] = 0x30; // jump to title 1
850             buf[offset + 1] = 0x02;
851             buf[offset + 2] = 0x00;
852             buf[offset + 3] = 0x00;
853             buf[offset + 4] = 0x00;
854             buf[offset + 5] = 0x01;
855             buf[offset + 6] = 0x00;
856             buf[offset + 7] = 0x00;
857           } /*if*/
858         buf[0x4ed] = 1; /* number of pre commands, low byte */
859       } /*if*/
860     write2(buf + 0x4f2, 7 + buf[0x4ed] * 8); /* end address relative to command table */
861     write2(buf + 0x82 /* end byte address, low word, of VMGI_MAT */, 0x4ec + read2(buf + 0x4f2));
862     nfwrite(buf, 2048, h);
863 
864     Create_TT_SRPT(h, ws->titlesets, vtsstart); /* generate it for real */
865 
866     // PGC
867     if (jumppad || forcemenus)
868         CreatePGC(h, ws, VTYPE_VMGM);
869 
870   /* VMG_VTS_ATRT contains copies of menu and title attributes from all titlesets */
871   /* output immediately following IFO header, as promised above */
872     memset(buf, 0, 2048);
873     j = 8 + ws->titlesets->numvts * 4;
874     write2(buf, ws->titlesets->numvts); /* number of titlesets */
875     write4(buf + 4, ws->titlesets->numvts * 0x30c + 8 - 1); /* end address (last byte of last VTS_ATRT) */
876     for (i = 0; i < ws->titlesets->numvts; i++)
877         write4(buf + 8 + i * 4, j + i * 0x308); /* offset to VTS_ATRT i */
878     nfwrite(buf, j, h);
879     for (i = 0; i < ws->titlesets->numvts; i++) /* output each VTS_ATRT */
880       {
881         write4(buf, 0x307); /* end address */
882         memcpy(buf + 4, ws->titlesets->vts[i].vtscat, 4);
883           /* VTS_CAT (copy of bytes 0x22 .. 0x25 of VTS IFO) */
884         memcpy(buf + 8, ws->titlesets->vts[i].vtssummary, 0x300);
885           /* copy of VTS attributes (bytes 0x100 onwards of VTS IFO) */
886         nfwrite(buf, 0x308, h);
887         j += 0x308;
888       } /*for*/
889     j = 2048 - (j & 2047);
890     if (j < 2048)
891       { /* pad to next whole sector */
892         nfpad(j, h);
893       } /*if*/
894 
895     if (jumppad || forcemenus)
896       {
897         CreateCellAddressTable(h, ws->menus->mg_vg); /* actually generate VMGM_C_ADT */
898         CreateVOBUAD(h, ws->menus->mg_vg); /* generate VMGM_VOBU_ADMAP */
899       } /*if*/
900   /* nfpad(ifo_pad, h); */ /* unneeded, genisoimage will fix it for me */
901     fflush(h);
902     if (errno != 0)
903       {
904         fprintf(stderr, "\nERR:  Error %d -- %s -- flushing VMGM\n", errno, strerror(errno));
905         exit(1);
906       } /*if*/
907     fclose(h);
908   } /*TocGen*/
909