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