1 /*
2     dvdauthor -- generation of .VOB 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 "compat.h"
24 
25 #include <assert.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <ctype.h>
29 
30 #include "dvdauthor.h"
31 #include "da-internal.h"
32 
33 
34 
35 struct colorremap /* for remapping colours to indexes into a common palette */
36   {
37     int newcolors[16]; /* bottom 24 bits are YCbCr, bit 24 is set to indicate a colour needs remapping */
38     int state,curoffs,maxlen,nextoffs,skip,ln_ctli; /* state of SPU parser machine (procremap) */
39     struct colorinfo *origmap; /* colours merged into common indexes into this palette */
40   };
41 
42 struct vscani {
43     int lastrefsect; /* flag that last sector should be recorded as a reference sector */
44     int firstgop; /* 1 => looking for first GOP, 2 => found first GOP, 0 => don't bother looking any more */
45     int firsttemporal; /* first temporal sequence number seen in current sequence */
46     int lastadjust; /* temporal sequence reset */
47 };
48 
49 static pts_t const timeline[19]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
50                            20,60,120,240};
51   /* various time steps for VOBU offsets needed in DSI packet, in units of half a second */
52 
53 #define BIGWRITEBUFLEN (16*2048)
54 static unsigned char bigwritebuf[BIGWRITEBUFLEN];
55 static int writebufpos=0;
56 static int writefile=-1; /* fd of output file */
57 
flushclose(int fd)58 static void flushclose(int fd)
59   /* ensures all data has been successfully written to disk before closing fd. */
60   {
61     if
62       (
63 #if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0
64         fdatasync(fd)
65 #else
66         fsync(fd)
67 #endif
68       )
69       {
70         if (errno != EINVAL)
71           {
72             fprintf(stderr, "ERR:  Error %d -- %s -- flushing output VOB\n", errno, strerror(errno));
73             exit(1);
74           }
75         else
76           {
77           /* non-flushable output */
78             errno = 0;
79           } /*if*/
80       } /*if*/
81     close(fd);
82   } /*flushclose*/
83 
84 static unsigned char videoslidebuf[15]={255,255,255,255, 255,255,255, 0,0,0,0, 0,0,0,0};
85 
86 
87 /* The following are variants for the ways I've seen DVD's encoded */
88 
89 // Grosse Pointe Blank uses exactly 1/2 second for the FF/REW records
90 // Bullitt uses 1/2 video second (i.e. in NTSC, 45045 PTS)
91 #define DVD_FFREW_HALFSEC 45000
92 // #define DVD_FFREW_HALFSEC (getratedenom(va)>>1)
93 
calcpts(const struct vobgroup * va,int cancomplain,int * didcomplain,pts_t * align,pts_t basepts,int nfields)94 static pts_t calcpts
95   (
96     const struct vobgroup *va,
97     int cancomplain,
98     int *didcomplain,
99     pts_t *align,
100     pts_t basepts,
101     int nfields /* count of prior fields */
102   )
103   /* returns basepts aligned to a whole number of fields, offset by *align. */
104   {
105     // I assume pts should round down?  That seems to be how mplex deals with it
106     // also see later comment
107     const pts_t fpts = getframepts(va);
108     const int bpframe = (basepts * 2 - *align + fpts / 2) / fpts; /* nearest whole field number */
109     if ((*align + bpframe * fpts) / 2 != basepts)
110       {
111         if (!*didcomplain)
112           {
113             if (cancomplain)
114                 fprintf(stderr, "WARN: Video PTS does not line up on a multiple of a field.\n");
115             *didcomplain = 1;
116           } /*if*/
117         *align = basepts * 2; /* assume this will avoid further warnings */
118       }
119     else
120         nfields += bpframe;
121     return (*align + nfields * fpts) / 2;
122   } /*calcpts*/
123 
findnextvideo(const struct vob * va,int cur,int dir)124 static int findnextvideo(const struct vob *va, int cur, int dir)
125   // find next (dir=1) or previous(dir=-1) vobu with video
126   {
127     int i, numvobus;
128     numvobus = va->numvobus;
129     switch(dir)
130       {
131     case 1:  // forward
132         for (i = cur+1; i < numvobus; i++)
133             if(va->vobu[i].hasvideo)
134                 return i;
135         return -1;
136     case -1: // backward
137         for (i = cur-1; i > -1; i--)
138             if(va->vobu[i].hasvideo)
139                 return i;
140         return -1;
141     default:
142         // ??
143         return -1;
144       } /*switch*/
145   } /*findnextvideo*/
146 
findaudsect(const struct vob * va,int aind,pts_t pts0,pts_t pts1)147 static int findaudsect(const struct vob *va, int aind, pts_t pts0, pts_t pts1)
148   /* finds the audpts entry, starting from aind, that includes the time pts0 .. pts1,
149     or -1 if not found. */
150   {
151     const struct audchannel * const ach = &va->audch[aind];
152     int l = 0, h = ach->numaudpts - 1;
153 
154     if (h < l)
155         return -1;
156     while (h > l)
157       {
158         const int m =(l + h + 1) / 2; /* binary search */
159         if (pts0 < ach->audpts[m].pts[0])
160             h = m - 1;
161         else
162             l = m;
163       } /*while*/
164     if (ach->audpts[l].pts[0] > pts1)
165         return -1;
166     return ach->audpts[l].asect;
167   } /*findaudsect*/
168 
findvobubysect(const struct vob * va,int sect)169 static int findvobubysect(const struct vob *va, int sect)
170   /* returns the index of the VOBU that spans the specified sector. */
171   {
172     int l = 0, h = va->numvobus - 1;
173     if (h < 0)
174         return -1;
175     if (sect < va->vobu[0].sector)
176         return -1;
177     while (l < h)
178       {
179         const int m = (l + h + 1) / 2; /* binary search */
180         if (sect < va->vobu[m].sector)
181             h = m - 1;
182         else
183             l = m;
184       } /*while*/
185     return l;
186   } /*findvobubysect*/
187 
findspuidx(const struct vob * va,int ach,pts_t pts0)188 static int findspuidx(const struct vob *va, int ach, pts_t pts0)
189   /* returns the index of the subpicture packet spanning the specified time. */
190   {
191     int l = 0, h = va->audch[ach].numaudpts - 1;
192     if (h < l)
193         return -1;
194     while (h > l)
195       {
196         const int m = (l + h + 1) / 2; /* binary search */
197         if (pts0 < va->audch[ach].audpts[m].pts[0])
198             h = m - 1;
199         else
200             l = m;
201       } /*while*/
202     return l;
203   } /*findspuidx*/
204 
getsect(const struct vob * va,int curvobunum,int jumpvobunum,int skip,unsigned notfound)205 static unsigned int getsect
206   (
207     const struct vob * va,
208     int curvobunum, /* the VOBU number I'm jumping from */
209     int jumpvobunum, /* the VOBU number I'm jumping to */
210     int skip, /* whether to set the skipping-more-than-one-VOBU bit */
211     unsigned notfound /* what to return if there is no matching VOBU */
212   )
213   /* computes relative VOBU offsets needed at various points in a DSI packet,
214     including the mask bit that indicates a backward jump, and optionally the
215     one indicating skipping multiple video VOBUs as well. */
216   {
217     if (skip)
218       {
219         int l, h, i;
220         // only set skip bit if one of the VOBU's from here to there contain video
221       /* hmm, this page <http://www.mpucoder.com/DVD/dsi_pkt.html> doesn't say
222         it has to contain video */
223         if (curvobunum < jumpvobunum)
224           {
225             l = curvobunum + 1;
226             h = jumpvobunum - 1;
227           }
228         else
229           {
230             l = jumpvobunum + 1;
231             h = curvobunum - 1;
232           } /*if*/
233         for (i = l; i <= h; i++)
234             if (va->vobu[i].hasvideo)
235                 break; /* found an in-between VOBU containing video */
236         if (i <= h)
237             skip = 0x40000000;
238         else
239             skip = 0;
240       } /*if*/
241     if
242       (
243           jumpvobunum < 0
244       ||
245           jumpvobunum >= va->numvobus
246       ||
247           va->vobu[jumpvobunum].vobcellid != va->vobu[curvobunum].vobcellid
248             /* never cross cells */
249       )
250         return
251             notfound | skip;
252     return
253             abs(va->vobu[jumpvobunum].sector - va->vobu[curvobunum].sector)
254         |
255             (va->vobu[jumpvobunum].hasvideo ? 0x80000000 : 0)
256         |
257             skip;
258   } /*getsect*/
259 
readscr(const unsigned char * buf)260 static pts_t readscr(const unsigned char *buf)
261   /* returns the timestamp as found in the pack header. This is actually supposed to
262     be units of a 27MHz clock, but I ignore the extra precision and truncate it to
263     the usual 90kHz clock units. */
264   {
265     return
266             ((pts_t)(buf[0] & 0x38)) << 27 /* SCR 32 .. 30 */
267         |
268             (buf[0] & 3) << 28 /* SCR 29 .. 28 */
269         |
270             buf[1] << 20 /* SCR 27 .. 20 */
271         |
272             (buf[2] & 0xf8) << 12 /* SCR 19 .. 15 */
273         |
274             (buf[2] & 3) << 13 /* SCR 14 .. 13 */
275         |
276             buf[3] << 5 /* SCR 12 .. 5 */
277         |
278             (buf[4] & 0xf8) >> 3; /* SCR 4 .. 0 */
279           /* ignore SCR_ext */
280   } /*readscr*/
281 
writescr(unsigned char * buf,pts_t scr)282 static void writescr(unsigned char *buf, pts_t scr)
283   /* writes a new timestamp for a pack header, ignoring the additional 27MHz
284     precision. */
285   {
286     buf[0] = ((scr >> 27) & 0x38) | ((scr >> 28) & 3) | 68;
287     buf[1] = scr >> 20;
288     buf[2] = ((scr >> 12) & 0xf8) | ((scr >> 13) & 3) | 4;
289     buf[3] = scr >> 5;
290     buf[4] = ((scr << 3) & 0xf8) | (buf[4] & 7);
291   } /*writescr*/
292 
readpts(const unsigned char * buf)293 static pts_t readpts(const unsigned char *buf)
294   /* reads a timestamp from a PES header as expressed in 90kHz clock units. */
295   {
296     int a1, a2, a3;
297     a1 = (buf[0] & 0xe) >> 1;
298     a2 = ((buf[1] << 8) | buf[2]) >> 1;
299     a3 = ((buf[3] << 8) | buf[4]) >> 1;
300     return
301             ((pts_t)a1) << 30
302         |
303             a2 << 15
304         |
305             a3;
306   } /*readpts*/
307 
writepts(unsigned char * buf,pts_t pts)308 static void writepts(unsigned char *buf, pts_t pts)
309   /* writes a timestamp to a PES header as expressed in 90kHz clock units. */
310   {
311     buf[0] = ((pts >> 29) & 0xe) | (buf[0] & 0xf1);
312       // this preserves the PTS / DTS / PTSwDTS top bits
313     write2(buf + 1, (pts >> 14) | 1);
314     write2(buf + 3, (pts << 1) | 1);
315   } /*writepts*/
316 
findbutton(const struct pgc * pg,const char * dest,int dflt)317 static int findbutton(const struct pgc *pg, const char *dest, int dflt)
318   /* returns the index of the button with name dest, or dflt if not found or no name specified. */
319   {
320     int i;
321     if (!dest)
322         return dflt;
323     for (i = 0; i < pg->numbuttons; i++)
324         if (!strcmp(pg->buttons[i].name,dest))
325             return i + 1;
326     return dflt;
327   } /*findbutton*/
328 
transpose_ts(unsigned char * buf,pts_t tsoffs)329 static void transpose_ts(unsigned char *buf, pts_t tsoffs)
330   /* adjusts the timestamps in the specified PACK header and its constituent packets
331     by the specified amount. */
332   {
333     // pack scr
334     if
335       (
336             buf[0] == 0
337         &&
338             buf[1] == 0
339         &&
340             buf[2] == 1
341         &&
342             buf[3] == MPID_PACK
343       )
344       {
345         const int sysoffs =
346             buf[14] == 0 && buf[15] == 0 && buf[16] == 1 && buf[17] == MPID_SYSTEM ?
347               /* skip system header if present */
348                 (buf[18] << 8 | buf[19]) + 6
349             :
350                 0;
351         writescr(buf + 4, readscr(buf + 4) + tsoffs);
352         // video/audio?
353         // pts?
354         if
355           (
356                 buf[14 + sysoffs] == 0
357             &&
358                 buf[15 + sysoffs] == 0
359             &&
360                 buf[16 + sysoffs] == 1
361             &&
362                 (
363                     buf[17 + sysoffs] == MPID_PRIVATE1
364                       /* audio or subpicture stream */
365                 ||
366                     buf[17 + sysoffs] >= MPID_AUDIO_FIRST && buf[17 + sysoffs] <= MPID_VIDEO_LAST
367                       /* audio or video stream */
368                 )
369             &&
370                 (buf[21 + sysoffs] & 128) /* PTS present */
371           )
372           {
373             writepts(buf + 23 + sysoffs, readpts(buf + 23 + sysoffs) + tsoffs);
374             // dts?
375             if (buf[21 + sysoffs] & 64) /* decoder timestamp present */
376               {
377                 writepts(buf + 28 + sysoffs, readpts(buf + 28 + sysoffs) + tsoffs);
378               } /*if*/
379           } /*if*/
380     }
381   } /*transpose_ts*/
382 
has_gop(const unsigned char * buf)383 static bool has_gop(const unsigned char *buf)
384   /* returns true iff there is a video GOP present in the sector in buf. */
385   {
386     if
387       (
388             buf[14] == 0
389         &&
390             buf[15] == 0
391         &&
392             buf[16] == 1
393         &&
394             buf[17] == MPID_SYSTEM
395         &&
396             buf[38] == 0
397         &&
398             buf[39] == 0
399         &&
400             buf[40] == 1
401         &&
402             buf[41] == MPID_VIDEO_FIRST
403       )
404       {
405         int i = 42;
406         while (i < 1024)
407           {
408             if
409               (
410                     buf[i] == 0
411                 &&
412                     buf[i + 1] == 0
413                 &&
414                     buf[i + 2] == 1
415                 &&
416                     buf[i + 3] == MPID_GOP
417               )
418                 return true;
419             i += 4;
420           } /*while*/
421       } /*if*/
422     return false;
423   } /*has_gop*/
424 
mpa_valid(const unsigned char * b)425 static bool mpa_valid(const unsigned char *b)
426   /* does b look like it points at a valid MPEG audio packet header. */
427   {
428     const unsigned int v = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
429     int t;
430     // sync, mpeg1, layer2, 48khz
431     if ((v & 0xFFFE0C00) != 0xFFFC0400)
432         return false;
433     // bitrate 1..14
434     t = (v >> 12) & 15;
435     if (t == 0 || t == 15)
436         return false;
437     // emphasis reserved
438     if ((v & 3) == 2)
439         return false;
440     return true;
441   } /*mpa_valid*/
442 
mpa_len(const unsigned char * b)443 static int mpa_len(const unsigned char *b)
444   /* returns the length of an MPEG audio packet. */
445   {
446     static int const bitratetable[16]={0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0};
447     const int padding =(b[2] >> 1) & 1;
448     const int bitrate = bitratetable[(b[2] >> 4) & 15];
449     return 3 * bitrate + padding; // 144 * bitrate / sampling; 144 / 48 = 3
450   } /*mpa_len*/
451 
writeflush()452 static void writeflush()
453   /* writes out the data buffered so far. */
454   {
455     if (!writebufpos) /* nothing in buffer */
456         return;
457     if (writefile != -1)
458       {
459         if (write(writefile, bigwritebuf, writebufpos) != writebufpos)
460           {
461             fprintf(stderr, "ERR:  Error %d -- %s -- writing data\n", errno, strerror(errno));
462             exit(1);
463           } /*if*/
464       } /*if*/
465     writebufpos = 0;
466   } /*writeflush*/
467 
writegrabbuf()468 static unsigned char *writegrabbuf()
469   /* returns the start address at which to write the next sector,
470     automatically flushing previously-written sectors as necessary. */
471   {
472     unsigned char *buf;
473     if (writebufpos == BIGWRITEBUFLEN)
474         writeflush();
475     buf=bigwritebuf+writebufpos;
476     writebufpos += 2048; /* sector will be written to output file */
477     return buf;
478   } /*writegrabbuf*/
479 
writeundo()480 static void writeundo()
481   /* drops the last sector from the output buffer. */
482   {
483     writebufpos -= 2048;
484   } /*writeundo*/
485 
writeclose()486 static void writeclose()
487   /* flushes and closes the output file. */
488   {
489     writeflush();
490     if (writefile != -1)
491       {
492         flushclose(writefile);
493         writefile = -1;
494       } /*if*/
495   } /*writeclose*/
496 
writeopen(const char * newname)497 static void writeopen(const char *newname)
498   /* opens an output file for writing. */
499   {
500     writefile = open(newname, O_CREAT | O_WRONLY | O_BINARY, 0666);
501     if (writefile < 0)
502       {
503         fprintf(stderr, "ERR:  Error %d opening %s: %s\n", errno, newname, strerror(errno));
504         exit(1);
505       } /*if*/
506   }
507 
closelastref(struct vobuinfo * thisvi,struct vscani * vsi,int cursect)508 static void closelastref(struct vobuinfo *thisvi, struct vscani *vsi, int cursect)
509   /* collects another end-sector of another reference frame, if I don't have enough already. */
510   {
511     if (vsi->lastrefsect && thisvi->numref < 3)
512       {
513         thisvi->lastrefsect[thisvi->numref++] = cursect;
514         vsi->lastrefsect = 0;
515       } /*if*/
516   } /*closelastref*/
517 
518 // this function is allowed to update buf[7] and guarantee it will end up
519 // in the output stream
520 // prevbytesect is the sector for the byte immediately preceding buf[0]
scanvideoptr(struct vobgroup * va,unsigned char * buf,struct vobuinfo * thisvi,int prevbytesect,struct vscani * vsi)521 static void scanvideoptr
522   (
523     struct vobgroup *va,
524     unsigned char *buf,
525     struct vobuinfo *thisvi,
526     int prevbytesect,
527     struct vscani *vsi
528   )
529   {
530     if
531       (
532             buf[0] == 0
533         &&
534             buf[1] == 0
535         &&
536             buf[2] == 1 /* looks like a packet header */
537       )
538       {
539         switch(buf[3])
540           {
541         case MPID_PICTURE: /* picture header */
542           {
543             const int ptype = (buf[5] >> 3) & 7; /* frame type, 1 => I, 2 => P, 3 => B, 4 => D */
544             const int temporal = (buf[4] << 2) | (buf[5] >> 6); /* temporal sequence number */
545             closelastref(thisvi, vsi, prevbytesect);
546             if (vsi->firsttemporal == -1)
547                 vsi->firsttemporal = temporal;
548             vsi->lastadjust = (temporal < vsi->firsttemporal);
549             if (ptype == 1 || ptype == 2) // I or P frame
550                 vsi->lastrefsect = 1; /* it's a reference frame */
551             if (va->vd.vmpeg == VM_MPEG1)
552               {
553                 thisvi->numfields += 2;
554                 if (vsi->lastadjust && vsi->firstgop == 2)
555                     thisvi->firstIfield += 2;
556               } /*if*/
557 
558             // fprintf(stderr,"INFO: frame type %d, tempref=%d, prevsect=%d\n",ptype,temporal,prevbytesect);
559         break;
560           } /*case 0*/
561 
562         case MPID_SEQUENCE: /* sequence header */
563           {
564           /* collect information about video attributes */
565             int hsize, vsize, aspect, framerate, newaspect;
566             char sizestring[30];
567             closelastref(thisvi, vsi, prevbytesect);
568             hsize = (buf[4] << 4) | (buf[5] >> 4);
569             vsize = ((buf[5] << 8) & 0xf00) | buf[6];
570             aspect = buf[7] >> 4;
571             framerate = buf[7] & 0xf;
572 
573             vobgroup_set_video_framerate(va, framerate);
574             switch (framerate)
575               {
576             case VR_NTSCFILM: // 24000/1001
577             case VR_NTSC: // 30000/1001
578             case VR_NTSCFIELD: // 60000/1001
579                 vobgroup_set_video_attr(va, VIDEO_FORMAT, "ntsc");
580             break;
581 
582             case VR_PAL: // 25
583             case VR_PALFIELD: // 50
584                 vobgroup_set_video_attr(va, VIDEO_FORMAT, "pal");
585             break;
586 
587             case VR_FILM: // 24
588             case VR_30: // 30
589             case VR_60: // 60
590                // these are nonstandard, but at least we know what they are
591             break;
592 
593             default:
594                 fprintf(stderr, "WARN: unknown frame rate %d\n", framerate);
595             break;
596               } /* switch(framerate) */
597             sprintf(sizestring, "%dx%d", hsize, vsize);
598             vobgroup_set_video_attr(va, VIDEO_RESOLUTION, sizestring);
599             if (va->vd.vmpeg == VM_MPEG1)
600               {
601                 switch (aspect)
602                   {
603                 case 3:
604                     vobgroup_set_video_attr(va, VIDEO_ASPECT, "16:9");
605                     vobgroup_set_video_attr(va, VIDEO_FORMAT, "pal");
606                 break;
607                 case 6:
608                     vobgroup_set_video_attr(va, VIDEO_ASPECT, "16:9");
609                     vobgroup_set_video_attr(va, VIDEO_FORMAT, "ntsc");
610                 break;
611                 case 8:
612                     vobgroup_set_video_attr(va, VIDEO_ASPECT, "4:3");
613                     vobgroup_set_video_attr(va, VIDEO_FORMAT, "pal");
614                 break;
615                 case 12:
616                     vobgroup_set_video_attr(va, VIDEO_ASPECT, "4:3");
617                     vobgroup_set_video_attr(va, VIDEO_FORMAT, "ntsc");
618                 break;
619                 default:
620                     fprintf(stderr,"WARN: unknown mpeg1 aspect ratio %d\n",aspect);
621                 break;
622                   } /*switch*/
623                 newaspect =
624                         3
625                     +
626                         (va->vd.vaspect == VA_4x3) * 5
627                     +
628                         (va->vd.vformat == VF_NTSC) * 3;
629                 if (newaspect == 11)
630                     newaspect++;
631                 buf[7] = (buf[7] & 0xf) | (newaspect << 4); // reset the aspect ratio
632               }
633             else if (va->vd.vmpeg == VM_MPEG2)
634               {
635                 if (aspect == 2)
636                     vobgroup_set_video_attr(va, VIDEO_ASPECT, "4:3");
637                 else if (aspect == 3)
638                     vobgroup_set_video_attr(va, VIDEO_ASPECT, "16:9");
639                 else
640                     fprintf(stderr, "WARN: unknown mpeg2 aspect ratio %d\n", aspect);
641                 buf[7] = (buf[7] & 0xf) | (va->vd.vaspect == VA_4x3 ? 2 : 3) << 4;
642                   // reset the aspect ratio
643               } /*if*/
644             break;
645           } /* case MPID_SEQUENCE */
646 
647         case MPID_EXTENSION: /* extension header */
648           {
649             vobgroup_set_video_attr(va, VIDEO_MPEG, "mpeg2");
650             switch (buf[4] & 0xF0)
651               {
652             case 0x10: // sequence extension
653                 closelastref(thisvi, vsi, prevbytesect);
654             break;
655 
656             case 0x20: // sequence display extension
657                 closelastref(thisvi, vsi, prevbytesect);
658                 switch (buf[4] & 0xE) /* video format */
659                   {
660                 case 2:
661                     vobgroup_set_video_attr(va, VIDEO_FORMAT, "pal");
662                 break;
663                 case 4:
664                     vobgroup_set_video_attr(va, VIDEO_FORMAT, "ntsc");
665                 break;
666                 // case 6: // secam
667                 // case 10: // unspecified
668                   }
669             break;
670 
671             case 0x80: // picture coding extension
672               {
673                 int padj = 1; // default field pic
674                 if ((buf[6] & 3) /* picture structure */ == 3)
675                     padj++; // adj for frame pic
676                 if (buf[7] & 2) /* repeat first field */
677                     padj++; // adj for repeat flag
678                 thisvi->numfields += padj;
679                 if (vsi->lastadjust && vsi->firstgop == 2)
680                     thisvi->firstIfield += padj;
681                 // fprintf(stderr,"INFO: repeat flag=%d, cursect=%d\n",buf[7]&2,cursect);
682               }
683             break;
684               } /*switch*/
685         break;
686           } /* case MPID_EXTENSION */
687 
688         case MPID_SEQUENCE_END:
689             thisvi->hasseqend = 1;
690         break;
691 
692         case MPID_GOP: // gop header
693             closelastref(thisvi, vsi, prevbytesect);
694             if (vsi->firstgop == 1)
695               {
696                 vsi->firstgop = 2; /* found first GOP */
697                 vsi->firsttemporal = -1;
698                 vsi->lastadjust = 0;
699               }
700             else if (vsi->firstgop == 2)
701               {
702                 vsi->firstgop = 0; /* no need to find any more GOPs */
703               } /*if*/
704             if (false)
705               {
706                 int hr, mi, se, fr;
707                 hr = (buf[4] >> 2) & 31;
708                 mi = ((buf[4] & 3) << 4) | (buf[5] >> 4);
709                 se = ((buf[5] & 7) << 3) | (buf[6] >> 5);
710                 fr = ((buf[6] & 31) << 1) | (buf[7] >> 7);
711                 fprintf
712                   (
713                     stderr,
714                     "INFO: GOP header, %d:%02d:%02d:%02d, drop=%d\n",
715                     hr, mi, se, fr, buf[4] >> 7
716                   );
717               } /*if*/
718         break; /* case MPID_GOP */
719           } /*switch*/
720     } /*if*/
721   } /*scanvideoptr*/
722 
scanvideoframe(struct vobgroup * va,unsigned char * buf,struct vobuinfo * thisvi,int cursect,int prevsect,struct vscani * vsi)723 static void scanvideoframe(struct vobgroup *va, unsigned char *buf, struct vobuinfo *thisvi, int cursect, int prevsect, struct vscani *vsi)
724  {
725     int i, f = 0x17 + buf[0x16], l = 0x14 + buf[0x12] * 256 + buf[0x13];
726     int mpf;
727     struct vobuinfo oldtvi;
728     struct vscani oldvsi;
729     if (l - f < 8)
730       {
731         memcpy(videoslidebuf + 7, buf + f, l - f);
732         for (i = 0; i < l - f; i++)
733             scanvideoptr(va, videoslidebuf + i, thisvi, prevsect, vsi);
734         memcpy(buf + f, videoslidebuf + 7, l - f);
735         memset(videoslidebuf, 255, 7);
736         return;
737       } /*if*/
738  rescan:
739     mpf = va->vd.vmpeg;
740     oldtvi = *thisvi;
741     oldvsi = *vsi;
742     // copy the first 7 bytes to use with the prev 7 bytes in hdr detection
743     memcpy(videoslidebuf + 7, buf + f, 8); // we scan the first header using the slide buffer
744     for (i = 0; i <= 7; i++)
745         scanvideoptr(va, videoslidebuf + i, thisvi, prevsect, vsi);
746     memcpy(buf + f, videoslidebuf + 7, 8);
747     // quickly scan all but the last 7 bytes for a hdr
748     // buf[f]... was already scanned in the videoslidebuffer to give the correct sector
749     for (i = f + 1; i < l - 7; i++)
750       {
751         if (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1)
752             scanvideoptr(va, buf + i, thisvi, cursect, vsi);
753       } /*for*/
754     if (!va->vd.vmpeg)
755         vobgroup_set_video_attr(va, VIDEO_MPEG, "mpeg1");
756     // if the mpeg version changed, then rerun scanvideoframe, because
757     // scanvideoptr updates the aspect ratio in the sequence header
758     if (mpf != va->vd.vmpeg)
759       {
760         *thisvi = oldtvi; // we must undo all the frame pointer changes
761         *vsi = oldvsi;
762         goto rescan;
763       } /*if*/
764     // use the last 7 bytes in the next iteration
765     memcpy(videoslidebuf, buf + l - 7, 7);
766   } /*scanvideoframe*/
767 
finishvideoscan(struct vobgroup * va,int vob,int prevsect,struct vscani * vsi)768 static void finishvideoscan(struct vobgroup *va, int vob, int prevsect, struct vscani *vsi)
769   {
770     struct vobuinfo * const lastvi = &va->vobs[vob]->vobu[va->vobs[vob]->numvobus - 1];
771     int i;
772     memset(videoslidebuf + 7, 0, 7);
773     for (i = 0; i < 7; i++)
774         scanvideoptr(va, videoslidebuf + i, lastvi, prevsect, vsi);
775     memset(videoslidebuf, 255, 7);
776     closelastref(lastvi, vsi, prevsect);
777   } /*finishvideoscan*/
778 
printpts(pts_t pts)779 static void printpts(pts_t pts)
780   /* displays a PTS value in seconds. */
781   {
782     fprintf(stderr,"%d.%03d",(int)(pts/90000),(int)((pts/90)%1000));
783   }
784 
785 enum /* states for SPU parser */
786   {
787     CR_BEGIN0,   CR_BEGIN1,    CR_BEGIN2,    CR_BEGIN3, CR_SKIP0,
788     CR_SKIP1,    CR_NEXTOFFS0, CR_NEXTOFFS1, CR_WAIT,   CR_CMD,
789     CR_SKIPWAIT, CR_COL0,      CR_COL1,      CR_CHGARG, CR_CHGLN0,
790     CR_CHGLN1,   CR_CHGLN2,    CR_CHGLN3,    CR_CHGPX0, CR_CHGPX1,
791     CR_CHGPX2,
792   };
793 
readpstr(const unsigned char * b,int * i)794 static char *readpstr(const unsigned char *b, int *i)
795 /* extracts a null-terminated string beginning at b[*i], advances *i past it and returns
796 a copy of the string. */
797   {
798     char *s = strdup((const char *)b + i[0]);
799     i[0] += strlen(s) + 1;
800     return s;
801   } /*readpstr*/
802 
initremap(struct colorremap * cr)803 static void initremap(struct colorremap *cr)
804   /* initializes the remap table to all identity mappings, and the state machine
805     ready to start processing a new SPU. */
806   {
807     int i;
808     for (i = 0; i < 16; i++) /* initially don't remap any colours */
809         cr->newcolors[i] = i;
810     cr->state = CR_BEGIN0;
811     cr->origmap = 0;
812   } /*initremap*/
813 
remapcolor(struct colorremap * cr,int idx)814 static int remapcolor(struct colorremap *cr, int idx)
815   /* returns the appropriate remapping of the colour with the specified index
816     to the corresponding index in cr->origmap. */
817   {
818     int i, nc;
819     if (cr->newcolors[idx] < 16)
820         return
821             cr->newcolors[idx]; /* remapping already worked out */
822     nc = cr->newcolors[idx] & 0xffffff; /* get colour to be remapped */
823     for (i = 0; i < 16; i++) /* find existing entry, if any */
824         if (cr->origmap->color[i] == nc) /* got one */
825           {
826             cr->newcolors[idx] = i;
827             return
828                 i;
829           } /*if; for*/
830     for (i = 0; i < 16; i++)
831       /* allocate a new entry in origmap for it, if there is room */
832         if (cr->origmap->color[i] == COLOR_UNUSED)
833           {
834             cr->origmap->color[i] = nc;
835             cr->newcolors[idx] = i;
836             return
837                 i;
838           } /*if; for */
839     fprintf(stderr, "ERR:  color map full, unable to allocate new colors.\n");
840     exit(1);
841   } /*remapcolor*/
842 
remapbyte(struct colorremap * cr,unsigned char * b)843 static void remapbyte(struct colorremap *cr, unsigned char *b)
844   /* remaps the two colours in the two nibbles of *b. */
845   {
846     b[0] = remapcolor(cr, b[0] & 15) | (remapcolor(cr, b[0] >> 4) << 4);
847   } /*remapbyte*/
848 
procremap(struct colorremap * cr,unsigned char * b,int blen,pts_t * timespan)849 static void procremap
850   (
851     struct colorremap *cr,
852     unsigned char *b,
853     int blen, /* length of SPU chunk beginning at b */
854     pts_t *timespan /* has duration of SPU display added to it */
855   )
856   /* interprets the subpicture stream in order to pick up the colour information so it
857     can be remapped. */
858   {
859     while(blen)
860       {
861         // fprintf(stderr,"INFO: state=%d, byte=%02x (%d)\n",cr->state,*b,*b);
862         switch(cr->state)
863           {
864         case CR_BEGIN0: /* pick up high byte of Sub-Picture Unit length */
865             cr->curoffs = 0;
866             cr->skip = 0;
867             cr->maxlen = b[0] * 256;
868             cr->state = CR_BEGIN1;
869         break;
870         case CR_BEGIN1: /* pick up low byte of Sub-Picture Unit length */
871             cr->maxlen += b[0];
872             cr->state = CR_BEGIN2;
873         break;
874         case CR_BEGIN2: /* pick up high byte of offset to SP_DCSQT */
875             cr->nextoffs = b[0] * 256;
876             cr->state = CR_BEGIN3;
877         break;
878         case CR_BEGIN3: /* pick up low byte of offset to SP_DCSQT */
879             cr->nextoffs += b[0];
880             cr->state = CR_WAIT;
881         break;
882         case CR_WAIT: /* skipping bytes until start of SP_DCSQT */
883             if (cr->curoffs == cr->maxlen)
884               {
885                 cr->state = CR_BEGIN0; /* finished this SPU */
886                 continue; // execute BEGIN0 right away
887               } /*if*/
888             if (cr->curoffs != cr->nextoffs)
889                 break;
890             cr->state = CR_SKIP0; /* found start of SP_DCSQT */
891         // fall through to CR_SKIP0
892         case CR_SKIP0: /* pick up high byte of SP_DCSQ_TM */
893             *timespan += 1024 * b[0] * 256;
894             cr->state = CR_SKIP1;
895         break;
896         case CR_SKIP1: /* pick up low byte of SP_DCSQ_TM */
897             *timespan += 1024 * b[0];
898             cr->state = CR_NEXTOFFS0;
899         break;
900         case CR_NEXTOFFS0: /* pick up high byte of offset to next SP_DCSQ */
901             cr->nextoffs = b[0] * 256;
902             cr->state = CR_NEXTOFFS1;
903         break;
904         case CR_NEXTOFFS1: /* pick up low byte of offset to next SP_DCSQ */
905             cr->nextoffs += b[0];
906             cr->state = CR_CMD; /* expecting first command */
907         break;
908         case CR_SKIPWAIT: /* skipping rest of current command */
909             if (cr->skip)
910               {
911                 cr->skip--;
912                 break;
913               } /*if*/
914             cr->state = CR_CMD;
915         // fall through to CR_CMD
916         case CR_CMD: /* pick up start of next command */
917             switch (*b)
918               {
919             case SPU_FSTA_DSP:
920             case SPU_STA_DSP:
921             case SPU_STP_DSP:
922               /* nothing to do */
923             break;
924             case SPU_SET_COLOR:
925                 cr->state = CR_COL0;
926             break;
927             case SPU_SET_CONTR:
928                 cr->skip = 2; /* no need to look at this */
929                 cr->state = CR_SKIPWAIT;
930             break;
931             case SPU_SET_DAREA:
932                 cr->skip = 6; /* no need to look at this */
933                 cr->state = CR_SKIPWAIT;
934             break;
935             case SPU_SET_DSPXA:
936                 cr->skip = 4; /* no need to look at this */
937                 cr->state = CR_SKIPWAIT;
938             break;
939             case SPU_CHG_COLCON:
940                 cr->skip = 2; /* skip size of parameter area */
941                 cr->state = CR_CHGARG;
942             break;
943             case SPU_CMD_END:
944                 cr->state = CR_WAIT; /* end of SP_DCSQ */
945             break;
946             default:
947                 fprintf(stderr, "ERR:  procremap encountered unknown subtitle command: %d\n",*b);
948                 exit(1);
949               } /*switch*/
950         break;
951         case CR_COL0: /* first and second of four colours for SET_COLOR */
952             remapbyte(cr, b);
953             cr->state = CR_COL1;
954         break;
955         case CR_COL1: /* third and fourth of four colours for SET_COLOR */
956             remapbyte(cr, b);
957             cr->state = CR_CMD;
958         break;
959         case CR_CHGARG: /* skipping size word for CHG_COLCON */
960             if (!--cr->skip)
961                 cr->state = CR_CHGLN0;
962         break;
963         case CR_CHGLN0: /* expecting first byte of LN_CTLI subcommand for CHG_COLCON */
964             cr->ln_ctli = b[0] << 24;
965             cr->state = CR_CHGLN1;
966         break;
967         case CR_CHGLN1:
968             cr->ln_ctli |= b[0] << 16;
969             cr->state = CR_CHGLN2;
970         break;
971         case CR_CHGLN2:
972             cr->ln_ctli |= b[0] << 8;
973             cr->state = CR_CHGLN3;
974         break;
975         case CR_CHGLN3:
976             cr->ln_ctli |= b[0]; /* got complete four bytes at start of LN_CTLI */
977             if (cr->ln_ctli == 0x0fffffff) /* end of CHG_COLCON parameter area */
978                 cr->state = CR_CMD;
979             else
980               {
981                 cr->ln_ctli >>= 12;
982                 cr->ln_ctli &= 0xf; /* number of PX_CTLI to follow */
983                 cr->skip = 2;
984                 cr->state = CR_CHGPX0;
985               } /*if*/
986         break;
987         case CR_CHGPX0: /* expecting starting column nr for PX_CTLI subcommand for CHG_COLCON */
988             if (!--cr->skip)
989               {
990                 cr->skip = 2;
991                 cr->state = CR_CHGPX1;
992               } /*if*/
993         break;
994         case CR_CHGPX1: /* expecting new colour values for PX_CTLI subcommand */
995             remapbyte(cr, b);
996             if (!--cr->skip)
997               {
998                 cr->skip = 2;
999                 cr->state = CR_CHGPX2;
1000               } /*if*/
1001         break;
1002         case CR_CHGPX2: /* expecting new contrast values for PX_CTLI subcommand */
1003             if (!--cr->skip)
1004               {
1005               /* done this PX_CTLI */
1006                 if (!--cr->ln_ctli)
1007                     cr->state = CR_CHGLN0; /* done this LN_CTLI */
1008                 else
1009                   {
1010                     cr->skip = 2;
1011                     cr->state = CR_CHGPX0; /* next PX_CTLI in this LN_CTLI */
1012                   } /*if*/
1013               } /*if*/
1014         break;
1015         default: /* shouldn't occur */
1016             assert(false);
1017           } /*switch*/
1018         cr->curoffs++;
1019         b++;
1020         blen--;
1021       } /*while*/
1022   } /*procremap*/
1023 
printvobustatus(struct vobgroup * va,int cursect,bool checknonempty)1024 static void printvobustatus(struct vobgroup *va, int cursect, bool checknonempty)
1025   /* report total number of VOBUs and PGCs seen so far, and how much of the
1026     input file has been processed. */
1027   {
1028     int j, nv = 0;
1029     for (j = 0; j < va->numvobs; j++)
1030         nv += va->vobs[j]->numvobus;
1031     // fprintf(stderr, "STAT: VOBU %d at %dMB, %d PGCs, %d:%02d:%02d\r", nv, cursect / 512, va->numallpgcs, total / 324000000, (total % 324000000) / 5400000, (total % 5400000) / 90000);
1032     fprintf(stderr, "STAT: VOBU %d at %dMB, %d PGCs\r", nv, cursect / 512, va->numallpgcs);
1033     if (checknonempty && nv == 0)
1034       {
1035         fprintf(stderr, "\nERR:  no VOBUs found.\n");
1036         exit(1);
1037       } /*if*/
1038   } /*printvobustatus*/
1039 
audio_scan_ac3(struct audchannel * ach,const unsigned char * buf,int sof,int len)1040 static void audio_scan_ac3(struct audchannel *ach, const unsigned char *buf, int sof, int len)
1041   /* gets information about AC3 audio. */
1042   {
1043     uint32_t parse;
1044     int acmod,lfeon,nch=0;
1045     char attr[4];
1046 
1047     if( sof+8>len ) // check if there's room to parse all the interesting info
1048         return;
1049     if( buf[sof]!=0x0b || buf[sof+1]!=0x77 ) // verify ac3 sync
1050         return;
1051     parse=read4(buf+sof+4);
1052     if( (parse>>30)!=0 ) { // must be 48kHz
1053         fprintf(stderr,"WARN: Unknown AC3 sample rate: %d\n",parse>>30);
1054     }
1055     audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_SAMPLERATE,"48khz");
1056   /* now figure out how many channels are present ... (yes, all this code) */
1057     parse<<=8;
1058     // check bsid
1059     if( (parse>>27)!=8 && (parse>>27)!=6 ) // must be v6 or v8
1060         return;
1061     parse<<=5;
1062     // ignore bsmod
1063     parse<<=3;
1064     // acmod gives # of channels
1065     acmod=(parse>>29);
1066     parse<<=3;
1067     if( (acmod&1) && (acmod!=1) ) /* centre channel present */
1068         parse<<=2; /* skip cmixlev */
1069     if( acmod&4 ) /* surround channel(s) present */
1070         parse<<=2; /* skip surmixlev */
1071     if( acmod==2 ) { /* simple stereo */
1072       /* process dsurmod */
1073         if( (parse>>30)==2 ) /* 2 => Dolby Surround encoded, 1 => not encoded, 0 => not indicated, 3 => reserved */
1074             audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_DOLBY,"surround");
1075         // else if( (parse>>30)==1 )
1076         // audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_DOLBY,"nosurround");
1077         parse<<=2;
1078     }
1079     lfeon=(parse>>31); /* low-frequency effects on */
1080     // calc # channels
1081     switch(acmod) {
1082     case 0: nch=2; break;
1083     case 1: nch=1; break;
1084     case 2: nch=2; break;
1085     case 3: nch=3; break;
1086     case 4: nch=3; break;
1087     case 5: nch=4; break;
1088     case 6: nch=4; break;
1089     case 7: nch=5; break;
1090     }
1091     if( lfeon ) nch++; /* include LFE channel */
1092     sprintf(attr,"%dch",nch);
1093     audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_CHANNELS,attr);
1094   } /*audio_scan_ac3*/
1095 
audio_scan_dts(struct audchannel * ach,const unsigned char * buf,int sof,int len)1096 static void audio_scan_dts(struct audchannel *ach,const unsigned char *buf,int sof,int len)
1097   /* gets information about DTS audio. */
1098 {
1099 /* fixme: could determine number of channels and sampling rate, but I'm not bothering for now */
1100 }
1101 
audio_scan_pcm(struct audchannel * ach,const unsigned char * buf,int len)1102 static void audio_scan_pcm(struct audchannel *ach,const unsigned char *buf,int len)
1103   /* gets information about LPCM audio. */
1104 {
1105     char attr[6];
1106 
1107     switch(buf[1]>>6) {
1108     case 0: audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_QUANT,"16bps"); break;
1109     case 1: audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_QUANT,"20bps"); break;
1110     case 2: audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_QUANT,"24bps"); break;
1111   /* case 3: illegal */
1112     }
1113     sprintf(attr,"%dkhz",48*(1+((buf[1]>>4)&1))); /* 48 or 96kHz */
1114     audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_SAMPLERATE,attr);
1115     sprintf(attr,"%dch",(buf[1]&7)+1); /* nr channels */
1116     audiodesc_set_audio_attr(&ach->ad,&ach->adwarn,AUDIO_CHANNELS,attr);
1117 }
1118 
FindVobus(const char * fbase,struct vobgroup * va,vtypes ismenu)1119 int FindVobus(const char *fbase, struct vobgroup *va, vtypes ismenu)
1120   /* collects audio/video/subpicture information, remaps subpicture colours and generates
1121     output VOB files for a menu or titleset, complete except for the NAV packs. */
1122   {
1123     unsigned char *buf;
1124     static unsigned char deferred_buf[2048];
1125     int cursect = 0; /* sector nr in input file */
1126     int fsect = -1; /* sector nr in current output VOB file, -ve => not opened yet */
1127     int vnum;
1128     int outnum = -(int)ismenu + 1; /* +ve for a titleset, in which case used to generate output VOB file names */
1129     int vobid =0;
1130     struct mp2info
1131       {
1132         int hdrptr; /* index at which packet header starts */
1133         unsigned char buf[6]; /* save partial packet in case it crosses sector boundaries */
1134       } mp2hdr[8]; /* enough for the allowed 8 audio streams */
1135     struct colorremap *crs;
1136 
1137     crs = malloc(sizeof(struct colorremap) * 32); /* enough for 32 subpicture streams */
1138     for (vnum = 0; vnum < va->numvobs; vnum++)
1139       {
1140         int i, j;
1141         int sysoffs;
1142         bool hadfirstvobu = false;
1143         pts_t backoffs = 0, lastscr = 0;
1144         bool fill_in_vobus = false, got_deferred_buf = false;
1145         struct vob * const thisvob = va->vobs[vnum];
1146         int prevvidsect = -1;
1147         struct vscani vsi;
1148         struct vfile vf;
1149         uint64_t inoffset;
1150         vsi.lastrefsect = 0;
1151         for (i = 0; i < 32; i++)
1152             initremap(crs + i);
1153 
1154         vobid++;
1155         thisvob->vobid = vobid;
1156         vsi.firstgop = 1;
1157 
1158         fprintf(stderr, "\nSTAT: Processing %s...\n", thisvob->fname);
1159         vf = varied_open(thisvob->fname, O_RDONLY, "input video file");
1160         inoffset = 0;
1161         memset(mp2hdr, 0, 8 * sizeof(struct mp2info));
1162         while (true)
1163           {
1164             if (fsect == 524272)
1165               {
1166               /* VOB file reached maximum allowed size */
1167                 writeclose();
1168                 if (outnum <= 0)
1169                   { /* menu VOB cannot be split */
1170                     fprintf(stderr, "\nERR:  Menu VOB reached 1gb at inoffset %#"PRIx64"\n", inoffset);
1171                     exit(1);
1172                   } /*if*/
1173                 outnum++; /* for naming next VOB file */
1174                 fsect = -1;
1175               } /*if*/
1176             buf = writegrabbuf();
1177             if (got_deferred_buf)
1178                 memcpy(buf, deferred_buf, 2048);
1179             else
1180               {
1181                 i = fread(buf, 1, 2048, vf.h);
1182                 if (i != 2048)
1183                   {
1184                     if (i == -1)
1185                       {
1186                         fprintf(stderr, "\nERR:  Error %d while reading at inoffset %#"PRIx64": %s\n", errno, inoffset, strerror(errno));
1187                       }
1188                     else if (i > 0) /* shouldn't occur */
1189                       {
1190                         fprintf(stderr, "\nERR:  Partial sector read (%d bytes at inoffset %#"PRIx64")\n", i, inoffset);
1191                       }
1192                     else
1193                       {
1194                         writeundo();
1195                         break;
1196                       } /*if*/
1197                     exit(1);
1198                   } /*if*/
1199               } /*if*/
1200             if
1201               (
1202                     buf[14] == 0
1203                 &&
1204                     buf[15] == 0
1205                 &&
1206                     buf[16] == 1
1207                 &&
1208                     buf[17] == MPID_PAD
1209                 &&
1210                     !strcmp((const char *)buf + 20, "dvdauthor-data") /* message from spumux */
1211               )
1212               {
1213                 // private dvdauthor data, interpret and remove from final stream
1214                 int i = 35;
1215                 if (buf[i] != 2)
1216                   {
1217                     fprintf(stderr, "ERR:  dvd info packet at inoffset %#"PRIx64" is unexpected version %d\n", inoffset, buf[i]);
1218                     exit(1);
1219                   } /*if*/
1220                 switch (buf[i + 1]) // packet type
1221                   {
1222                 case 1: // subtitle/menu color and button information
1223                   {
1224                     int substreamid = buf[i + 2] & 31;
1225                     i += 3;
1226                     i += 8; // skip start pts and end pts
1227                     while (buf[i] != 0xff)
1228                       {
1229                         switch (buf[i])
1230                           {
1231                         case 1: // new colormap
1232                           {
1233                             int j;
1234                             crs[substreamid].origmap = thisvob->progchain->colors;
1235                               /* where to merge colours into */
1236                             for (j = 0; j < buf[i + 1]; j++)
1237                               {
1238                               /* collect colours needing remapping, which won't happen
1239                                 until they're actually referenced */
1240                                 crs[substreamid].newcolors[j] =
1241                                         COLOR_UNUSED /* indicate colour needs remapping */
1242                                     |
1243                                         buf[i + 2 + 3 * j] << 16
1244                                     |
1245                                         buf[i + 3 + 3 * j] << 8
1246                                     |
1247                                         buf[i + 4 + 3 * j];
1248                               } /*for*/
1249                             for (; j < 16; j++) /* fill in unused entries with identity mapping */
1250                                 crs[substreamid].newcolors[j] = j;
1251                             i += 2 + 3 * buf[i + 1];
1252                           }
1253                         break;
1254                         case 2: // new buttoncoli
1255                           {
1256                             int j;
1257                             memcpy(thisvob->buttoncoli, buf + i + 2, buf[i + 1] * 8);
1258                             for (j = 0; j < buf[i + 1]; j++)
1259                               {
1260                               /* remap the colours, not the contrast values */
1261                                 remapbyte(&crs[substreamid], thisvob->buttoncoli + j * 8 + 0);
1262                                 remapbyte(&crs[substreamid], thisvob->buttoncoli + j * 8 + 1);
1263                                 remapbyte(&crs[substreamid], thisvob->buttoncoli + j * 8 + 4);
1264                                 remapbyte(&crs[substreamid], thisvob->buttoncoli + j * 8 + 5);
1265                               } /*for*/
1266                             i += 2 + 8 * buf[i + 1];
1267                           }
1268                         break;
1269                         case 3: // button position information
1270                           {
1271                             int j;
1272                             const int nrbuttons = buf[i + 1];
1273                             i += 2;
1274                             for (j = 0; j < nrbuttons; j++)
1275                               {
1276                                 struct button * b;
1277                                 struct buttoninfo * bi, bitmp;
1278                                 char * const bn = readpstr(buf, &i);
1279                                 if (!findbutton(thisvob->progchain, bn, 0))
1280                                   {
1281                                     fprintf
1282                                       (
1283                                         stderr,
1284                                         "ERR:  Cannot find button '%s' as referenced by"
1285                                             " the subtitle at inoffset %#"PRIx64"\n",
1286                                         bn,
1287                                         inoffset
1288                                       );
1289                                     exit(1);
1290                                   } /*if*/
1291                                 b = &thisvob->progchain->buttons[findbutton(thisvob->progchain, bn, 0) - 1];
1292                                 free(bn);
1293 
1294                                 if (b->numstream >= MAXBUTTONSTREAM)
1295                                   {
1296                                     fprintf
1297                                       (
1298                                         stderr,
1299                                         "WARN: Too many button streams at inoffset %#"PRIx64";"
1300                                             " ignoring buttons\n",
1301                                         inoffset
1302                                       );
1303                                     bi = &bitmp; /* place to put discarded data */
1304                                   }
1305                                 else
1306                                   {
1307                                     bi = &b->stream[b->numstream++];
1308                                   } /*if*/
1309                                 bi->substreamid = substreamid;
1310                                 i += 2; // skip modifier
1311                                 bi->autoaction = buf[i++] != 0;
1312                                 bi->grp = buf[i];
1313                                 bi->x1 = read2(buf + i + 1);
1314                                 bi->y1 = read2(buf + i + 3);
1315                                 bi->x2 = read2(buf + i + 5);
1316                                 bi->y2 = read2(buf + i + 7);
1317                                 i += 9;
1318                               /* neighbouring button names */
1319                                 bi->up = readpstr(buf, &i);
1320                                 bi->down = readpstr(buf, &i);
1321                                 bi->left = readpstr(buf, &i);
1322                                 bi->right = readpstr(buf, &i);
1323                               } /*for*/
1324                           } /*case 3*/
1325                         break;
1326                         default:
1327                             fprintf
1328                               (
1329                                 stderr,
1330                                 "ERR:  dvd info packet command within subtitle at inoffset %#"PRIx64": %d\n",
1331                                 inoffset,
1332                                 buf[i]
1333                               );
1334                             exit(1);
1335                           } /*switch*/
1336                       } /*while*/
1337 
1338                   } /*case 1*/
1339                 break;
1340 
1341                 default:
1342                     fprintf
1343                       (
1344                         stderr,
1345                         "ERR:  unknown dvdauthor-data packet type at inoffset %#"PRIx64": %d\n",
1346                         inoffset,
1347                         buf[i + 1]
1348                       );
1349                     exit(1);
1350                 } /*switch*/
1351 
1352                 writeundo(); /* drop private data from output */
1353                 continue;
1354               } /*if*/
1355             // we should get a VOBU before a video with GOP
1356             if
1357               (
1358                     (fill_in_vobus || !hadfirstvobu)
1359                 &&
1360                     !got_deferred_buf
1361                 &&
1362                     has_gop(buf)
1363               )
1364               {
1365                 // create VOBU, from Martin Crossley
1366                 if (!hadfirstvobu)
1367                   { /* let user know the first time this happens */
1368                     fprintf
1369                       (
1370                         stderr,
1371                         "INFO: found video GOP at inoffset %#"PRIx64" without a preceding VOBU"
1372                             " - creating VOBU\n",
1373                         inoffset
1374                       );
1375                   } /*if*/
1376                 fill_in_vobus = true; /* keep doing it from now on */
1377                 memcpy(deferred_buf, buf, 2048); /* save just-read sector for processing on next iteration */
1378                 got_deferred_buf = true; /* remember I've saved it */
1379               /* buf already has a system header */
1380                 buf[41] = MPID_PRIVATE2;
1381                 buf[42] = 0x03;
1382                 buf[43] = 0xd4;
1383                 buf[44] = 0x81; /* rest of PCI will be correctly filled in later by FixVobus */
1384                 memset(buf + 45, 0, 2048 - 45);
1385                 buf[1026] = 1;
1386                 buf[1027] = MPID_PRIVATE2;
1387                 buf[1028] = 0x03;
1388                 buf[1029] = 0xfa;
1389                 buf[1030] = 0x81; /* rest of DSI will be correctly filled in later by FixVobus */
1390               }
1391             else if (got_deferred_buf)
1392                 got_deferred_buf = false; /* already picked it up */
1393             if (buf[0] == 0 && buf[1] == 0 && buf[2] == 1 && buf[3] == MPID_PACK)
1394               {
1395                 const pts_t newscr = readscr(buf + 4);
1396                 if (hadfirstvobu && newscr == 0 && lastscr > 0)
1397                   /* suggestion from Philippe Sarazin -- alternatively, Shaun Jackman suggests
1398                     simply treating newscr < lastscr as a warning and continuing */
1399                   {
1400                     backoffs -= lastscr; /* adjust to remove SCR discontinuity */
1401                     fprintf(stderr, "\nWARN: SCR reset at inoffset %#"PRIx64". New back offset = %" PRId64"\n", inoffset, backoffs);
1402                   }
1403                 else if (newscr < lastscr)
1404                   {
1405                     fprintf
1406                       (
1407                         stderr,
1408                         "ERR:  SCR moves backwards at inoffset %#"PRIx64","
1409                             " remultiplex input: %" PRId64" < %" PRId64"\n",
1410                         inoffset,
1411                         newscr,
1412                         lastscr
1413                       );
1414                     exit(1);
1415                   } /*if*/
1416                 lastscr = newscr;
1417                 if (!hadfirstvobu)
1418                     backoffs = newscr; /* start SCR from 0 */
1419               } /*if*/
1420             transpose_ts(buf, -backoffs);
1421             if (fsect == -1)
1422               {
1423               /* start a new VOB file */
1424                 fsect = 0;
1425                 if (fbase)
1426                   {
1427                     char * newname;
1428                     if (outnum >= 0)
1429                       {
1430                         newname = sprintf_alloc("%s_%d.VOB", fbase, outnum);
1431                       }
1432                     else
1433                       {
1434                         newname = strdup(fbase);
1435                       } /*if*/
1436                     writeopen(newname);
1437                     free(newname);
1438                   } /*if*/
1439               } /*if*/
1440             if
1441               (
1442                     buf[14] == 0
1443                 &&
1444                     buf[15] == 0
1445                 &&
1446                     buf[16] == 1
1447                 &&
1448                     buf[17] == MPID_SYSTEM
1449               )
1450               {
1451                 if
1452                   (
1453                         buf[38] == 0
1454                     &&
1455                         buf[39] == 0
1456                     &&
1457                         buf[40] == 1
1458                     &&
1459                         buf[41] == MPID_PRIVATE2 // 1st private2
1460                     &&
1461                         buf[1024] == 0
1462                     &&
1463                         buf[1025] == 0
1464                     &&
1465                         buf[1026] == 1
1466                     &&
1467                         buf[1027] == MPID_PRIVATE2 // 2nd private2
1468                   ) /* looks like a NAV PACK, which means the start of a new VOBU */
1469                   {
1470                     struct vobuinfo *vi;
1471                     if (thisvob->numvobus)
1472                         finishvideoscan(va, vnum, prevvidsect, &vsi);
1473                     // fprintf(stderr, "INFO: vobu at inoffset %#"PRIx64"\n", inoffset);
1474                     hadfirstvobu = true; /* NAV PACK starts a VOBU */
1475                     if (thisvob->numvobus == thisvob->maxvobus) /* need more space */
1476                       {
1477                         if (!thisvob->maxvobus)
1478                             thisvob->maxvobus = 1; /* first allocation */
1479                         else
1480                             thisvob->maxvobus <<= 1;
1481                               /* resize in powers of 2 to reduce reallocation calls */
1482                         thisvob->vobu = (struct vobuinfo *)realloc
1483                           (
1484                             /*ptr =*/ thisvob->vobu,
1485                             /*size =*/ thisvob->maxvobus * sizeof(struct vobuinfo)
1486                           );
1487                       } /*if*/
1488                     vi = &thisvob->vobu[thisvob->numvobus]; /* for the new VOBU */
1489                     memset(vi, 0, sizeof(struct vobuinfo));
1490                     vi->sector = cursect;
1491                     vi->fsect = fsect;
1492                     vi->fnum = outnum;
1493                     vi->firstvideopts = -1;
1494                     vi->firstIfield = 0;
1495                     vi->numfields = 0;
1496                     vi->numref = 0;
1497                     vi->hasseqend = 0;
1498                     vi->hasvideo = 0;
1499                     memcpy(thisvob->vobu[thisvob->numvobus].sectdata, buf, 0x26); // save pack and system header; the rest will be reconstructed later
1500                     thisvob->numvobus++;
1501                     if (!(thisvob->numvobus & 15)) /* time to let user know progress */
1502                         printvobustatus(va, cursect, false);
1503                     vsi.lastrefsect = 0;
1504                     vsi.firstgop = 1; /* restart scan for first GOP */
1505                   } /*if*/
1506               } /*if*/
1507             if (!hadfirstvobu)
1508               {
1509                 fprintf
1510                   (
1511                     stderr,
1512                     "WARN: Skipping sector at inoffset %#"PRIx64", waiting for first VOBU...\n",
1513                     inoffset
1514                   );
1515                 writeundo(); /* ignore it */
1516                 continue;
1517               } /*if*/
1518             thisvob->vobu[thisvob->numvobus - 1].lastsector = cursect;
1519 
1520             i = 14;
1521             j = -1;
1522             while (i <= 2044)
1523               {
1524                 if (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1)
1525                   {
1526                     if (buf[i + 3] >= MPID_PRIVATE1 && buf[i + 3] <= MPID_VIDEO_LAST)
1527                       /* private, padding, audio or video stream */
1528                       {
1529                         j = i;
1530                         i += 6 + read2(buf + i + 4); /* start of next packet */
1531                         continue;
1532                       }
1533                     else if
1534                       (
1535                             buf[i + 3] == MPID_PROGRAM_END
1536                         &&
1537                             j >= 14
1538                         &&
1539                             buf[j + 3] == MPID_PAD /* previous was padding stream */
1540                       )
1541                       {
1542                         write2(buf + j + 4, read2(buf + j + 4) + 4);
1543                           /* merge program-end packet into prior pad packet */
1544                         memset(buf + i, 0, 4); // mplex uses 0 for padding, so will I
1545                       } /*if*/
1546                   } /*if*/
1547                 break;
1548               } /*while*/
1549 
1550             sysoffs =
1551                 buf[14] == 0 && buf[15] == 0 && buf[16] == 1 && buf[17] == MPID_SYSTEM ?
1552                   /* skip system header if present */
1553                     (buf[18] << 8 | buf[19]) + 6
1554                 :
1555                     0;
1556             if
1557               (
1558                     buf[0] == 0
1559                 &&
1560                     buf[1] == 0
1561                 &&
1562                     buf[2] == 1
1563                 &&
1564                     buf[3] == MPID_PACK
1565                 &&
1566                     buf[14 + sysoffs] == 0
1567                 &&
1568                     buf[15 + sysoffs] == 0
1569                 &&
1570                     buf[16 + sysoffs] == 1
1571                 &&
1572                     buf[17 + sysoffs] == MPID_VIDEO_FIRST /* only video stream */
1573               )
1574               {
1575                 struct vobuinfo * const vi = &thisvob->vobu[thisvob->numvobus - 1];
1576                 vi->hasvideo = 1;
1577                 scanvideoframe(va, buf + sysoffs, vi, cursect, prevvidsect, &vsi);
1578                 if
1579                   (
1580                         (buf[21 + sysoffs] & 128) /* PTS present */
1581                     &&
1582                         vi->firstvideopts == -1 /* not seen one yet */
1583                   )
1584                   {
1585                     vi->firstvideopts = readpts(buf + 23 + sysoffs);
1586                   } /*if*/
1587                 prevvidsect = cursect;
1588               } /*if*/
1589             if
1590               (
1591                     buf[0] == 0
1592                 &&
1593                     buf[1] == 0
1594                 &&
1595                     buf[2] == 1
1596                 &&
1597                     buf[3] == MPID_PACK
1598                 &&
1599                     buf[14 + sysoffs] == 0
1600                 &&
1601                     buf[15 + sysoffs] == 0
1602                 &&
1603                     buf[16 + sysoffs] == 1
1604                 &&
1605                     (
1606                         (buf[17 + sysoffs] & 0xf8) == 0xc0 /* MPEG audio stream */
1607                     ||
1608                         buf[17 + sysoffs] == MPID_PRIVATE1 /* DVD audio or subpicture */
1609                     )
1610               )
1611               {
1612                 pts_t pts0 = 0, pts1 = 0, backpts1 = 0;
1613                 const int dptr = buf[22 + sysoffs] /* PES header data length */ + 23 + sysoffs; /* offset to packet data */
1614                 const int endop = read2(buf + 18 + sysoffs) /* PES packet length */ + 20 /* fixed PES header length */ + sysoffs; /* end of packet */
1615                 int audch;
1616                 const int haspts = (buf[21 + sysoffs] & 128) != 0;
1617                 if (buf[17 + sysoffs] == MPID_PRIVATE1) /* DVD audio or subpicture */
1618                   {
1619                     const int sid = buf[dptr]; /* sub-stream ID */
1620                     const int offs = read2(buf + dptr + 2);
1621                       /* offset to audio sample frame which corresponds to PTS value */
1622                     const int nrframes = buf[dptr + 1];
1623                       /* nr audio sample frames beginning in this packet */
1624                     switch (sid & 0xf8)
1625                       {
1626                     case 0x20:                          // subpicture
1627                     case 0x28:                          // subpicture
1628                     case 0x30:                          // subpicture
1629                     case 0x38:                          // subpicture
1630                          audch = sid;
1631                     break;
1632                     case 0x80:                          // ac3 audio
1633                         pts1 += 2880 * nrframes;
1634                         audch = sid & 7;
1635                         audio_scan_ac3(&thisvob->audch[audch], buf + dptr + 4, offs - 1, endop - (dptr + 4));
1636                     break;
1637                     case 0x88:                          // dts audio
1638                       /* pts1 += 960 * nrframes; */ /* why not? */
1639                         audch = 24 | (sid & 7);
1640                         audio_scan_dts(&thisvob->audch[audch], buf + dptr + 4, offs - 1, endop - (dptr + 4));
1641                     break;
1642                     case 0xa0:                          // pcm audio
1643                         pts1 += 150 * nrframes;
1644                         audch = 16 | (sid & 7);
1645                         audio_scan_pcm(&thisvob->audch[audch], buf + dptr + 4, endop - (dptr + 4));
1646                     break;
1647                     default:         // unknown
1648                         audch = -1;
1649                     break;
1650                       } /*switch*/
1651                   }
1652                 else /* regular MPEG audio */
1653                   {
1654                     const int len = endop - dptr; /* length of packet data */
1655                     const int index = buf[17 + sysoffs] & 7; /* audio stream ID */
1656                     audch = 8 | index;                      // mp2
1657                     memcpy(mp2hdr[index].buf + 3, buf + dptr, 3);
1658                     while (mp2hdr[index].hdrptr + 4 <= len)
1659                       {
1660                         const unsigned char * h;
1661                         if (mp2hdr[index].hdrptr < 0)
1662                             h = mp2hdr[index].buf + 3 + mp2hdr[index].hdrptr;
1663                               /* overlap from previous */
1664                         else
1665                             h = buf + dptr + mp2hdr[index].hdrptr;
1666                         if (!mpa_valid(h))
1667                           {
1668                             mp2hdr[index].hdrptr++; /* try the next likely offset */
1669                             continue;
1670                           } /*if*/
1671                         if (mp2hdr[index].hdrptr < 0)
1672                             backpts1 += 2160; /* how much time to add to end of previous packet */
1673                         else
1674                             pts1 += 2160;
1675                         mp2hdr[index].hdrptr += mpa_len(h); /* to next header */
1676                       } /*while*/
1677                     mp2hdr[index].hdrptr -= len; /* will be -ve if extends into next sector */
1678                     memcpy(mp2hdr[index].buf, buf + dptr + len - 3, 3);
1679                     audiodesc_set_audio_attr(&thisvob->audch[audch].ad, &thisvob->audch[audch]. adwarn, AUDIO_SAMPLERATE, "48khz");
1680                   } /*if*/
1681               /* at this point, pts1 is the duration of the audio in the packet (0 for subpicture) */
1682                 if (haspts)
1683                   {
1684                     pts0 = readpts(buf + 23 + sysoffs);
1685                     pts1 += pts0;
1686                   }
1687                 else if (pts1 > 0)
1688                   {
1689                     fprintf
1690                       (
1691                         stderr,
1692                         "WARN: Audio channel %d contains sync headers at inoffset %#"PRIx64" but has no PTS.\n",
1693                         audch,
1694                         inoffset
1695                       );
1696                   } /*if*/
1697                 // fprintf(stderr,"aud ch=%d pts %d - %d (%d)\n",audch,pts0,pts1,pts1-pts0);
1698                 // fprintf(stderr,"pts[%d] %d (%02x %02x %02x %02x %02x)\n",va->numaudpts,pts,buf[23],buf[24],buf[25],buf[26],buf[27]);
1699                 if (audch < 0 || audch >= 64)
1700                   {
1701                     fprintf(stderr,"WARN: Invalid audio channel %d at inoffset %#"PRIx64"\n", audch, inoffset);
1702                   /* and ignore */
1703                   }
1704                 else if (haspts)
1705                   {
1706                     struct audchannel * const ach = &thisvob->audch[audch];
1707                     if (ach->numaudpts == ach->maxaudpts) { /* need more space */
1708                         if (ach->maxaudpts)
1709                             ach->maxaudpts <<= 1;
1710                               /* resize in powers of 2 to reduce reallocation calls */
1711                         else
1712                             ach->maxaudpts = 1; /* first allocation */
1713                         ach->audpts = (struct audpts *)realloc
1714                           (
1715                             /*ptr =*/ ach->audpts,
1716                             /*size =*/ ach->maxaudpts * sizeof(struct audpts)
1717                           );
1718                     } /*if*/
1719                     if (ach->numaudpts)
1720                       {
1721                         // we cannot compute the length of a DTS audio packet
1722                         // so just backfill if it is one
1723                         // otherwise, for mp2 add any pts to the previous
1724                         // sector for a header that spanned two sectors
1725                         if ((audch & 0x38) == 0x18) // is this DTS?
1726                             ach->audpts[ach->numaudpts - 1].pts[1] = pts0;
1727                         else
1728                             ach->audpts[ach->numaudpts - 1].pts[1] += backpts1;
1729 
1730                         if (ach->audpts[ach->numaudpts - 1].pts[1] < pts0)
1731                           {
1732                             if (audch >= 32)
1733                                 goto noshow; /* not audio */
1734                             fprintf
1735                               (
1736                                 stderr,
1737                                 "WARN: Discontinuity of %" PRId64" in audio channel %d"
1738                                     " at inoffset %#"PRIx64"; please remultiplex input.\n",
1739                                 pts0 - ach->audpts[ach->numaudpts - 1].pts[1],
1740                                 audch,
1741                                 inoffset
1742                               );
1743                             // fprintf(stderr,"last=%d, this=%d\n",ach->audpts[ach->numaudpts-1].pts[1],pts0);
1744                           }
1745                         else if (ach->audpts[ach->numaudpts - 1].pts[1] > pts0)
1746                             fprintf
1747                               (
1748                                 stderr,
1749                                 "WARN: %s pts for channel %d moves backwards by %"
1750                                     PRId64 " at inoffset %#"PRIx64"; please remultiplex input.\n",
1751                                 audch >= 32 ? "Subpicture" : "Audio",
1752                                 audch,
1753                                 ach->audpts[ach->numaudpts - 1].pts[1] - pts0,
1754                                 inoffset
1755                               );
1756                         else
1757                             goto noshow;
1758                         fprintf(stderr, "WARN: Previous sector: ");
1759                         printpts(ach->audpts[ach->numaudpts - 1].pts[0]);
1760                         fprintf(stderr, " - ");
1761                         printpts(ach->audpts[ach->numaudpts - 1].pts[1]);
1762                         fprintf(stderr, "\nWARN: Current sector: ");
1763                         printpts(pts0);
1764                         fprintf(stderr, " - ");
1765                         printpts(pts1);
1766                         fprintf(stderr, "\n");
1767                         ach->audpts[ach->numaudpts - 1].pts[1] = pts0;
1768                       } /*if*/
1769 noshow:
1770                   /* fill in new entry */
1771                     ach->audpts[ach->numaudpts].pts[0] = pts0;
1772                     ach->audpts[ach->numaudpts].pts[1] = pts1;
1773                     ach->audpts[ach->numaudpts].asect = cursect;
1774                     ach->numaudpts++;
1775                   } /*if*/
1776               } /*if*/
1777             // the following code scans subtitle code in order to
1778             // remap the colors and update the end pts
1779             if
1780               (
1781                     buf[0] == 0
1782                 &&
1783                     buf[1] == 0
1784                 &&
1785                     buf[2] == 1
1786                 &&
1787                     buf[3] == MPID_PACK
1788                 &&
1789                     buf[14 + sysoffs] == 0
1790                 &&
1791                     buf[15 + sysoffs] == 0
1792                 &&
1793                     buf[16 + sysoffs] == 1
1794                 &&
1795                     buf[17 + sysoffs] == MPID_PRIVATE1
1796               )
1797               {
1798                 int dptr = buf[22 + sysoffs] /* PES header data length */ + 23 + sysoffs; /* offset to packet data */
1799                 const int ml = read2(buf + 18) /* PES packet length */ + 20 /* fixed PES header length */ + sysoffs; /* end of packet */
1800                 const int st = buf[dptr]; /* sub-stream ID */
1801                 dptr++; /* skip sub-stream ID */
1802                 if ((st & 0xe0) == 0x20)
1803                   { /* subpicture stream */
1804                     procremap
1805                       (
1806                         /*cr =*/ &crs[st & 31],
1807                         /*b =*/ buf + dptr,
1808                         /*blen =*/ ml - dptr,
1809                         /*timespan =*/
1810                             &thisvob->audch[st].audpts[thisvob->audch[st].numaudpts - 1].pts[1]
1811                       );
1812                   } /*if*/
1813               } /*if*/
1814             cursect++;
1815             fsect++;
1816             inoffset += 2048;
1817           } /*while*/
1818         varied_close(vf);
1819         if (thisvob->numvobus)
1820           {
1821             int i;
1822             pts_t finalaudiopts;
1823             finishvideoscan(va, vnum, prevvidsect, &vsi);
1824             // find end of audio
1825             finalaudiopts = -1;
1826             for (i = 0; i < 32; i++)
1827               {
1828                 struct audchannel * const ach = thisvob->audch + i;
1829                 if
1830                   (
1831                         ach->numaudpts
1832                     &&
1833                         ach->audpts[ach->numaudpts - 1].pts[1] > finalaudiopts
1834                   )
1835                     finalaudiopts = ach->audpts[ach->numaudpts - 1].pts[1];
1836               } /*for*/
1837             // pin down all video vobus
1838             // note: we make two passes; one assumes that the PTS for the
1839             // first frame is exact; the other assumes that the PTS for
1840             // the first frame is off by 1/2.  If both fail, then the third pass
1841             // assumes things are exact and throws a warning
1842             for (i = 0; i < 3; i++)
1843               {
1844                 pts_t pts_align = -1; /* initially undefined */
1845                 int complained = 0, j;
1846                 for (j = 0; j < thisvob->numvobus; j++)
1847                   {
1848                     struct vobuinfo * const vi = thisvob->vobu + j;
1849                     if (vi->hasvideo)
1850                       {
1851                         if (pts_align == -1)
1852                           {
1853                             pts_align = vi->firstvideopts * 2;
1854                             if (i == 1)
1855                               {
1856                                 // I assume pts should round down?  That seems to be how mplex deals with it
1857                                 // also see earlier comment
1858 
1859                                 // since pts round down, then the alternative base we should try is
1860                                 // firstvideopts+0.5, thus increment
1861                                 pts_align++;
1862                               } /*if*/
1863                             // MarkChapters will complain if firstIfield!=0
1864                           } /*if*/
1865 
1866                         vi->videopts[0] = calcpts(va, i == 2, &complained, &pts_align, vi->firstvideopts, -vi->firstIfield);
1867                         vi->videopts[1] = calcpts(va, i == 2, &complained, &pts_align, vi->firstvideopts, -vi->firstIfield + vi->numfields);
1868                         // if this looks like a dud, abort and try the next pass
1869                         if (complained && i < 2)
1870                             break;
1871                         vi->sectpts[0] = vi->videopts[0];
1872                         if (j + 1 == thisvob->numvobus && finalaudiopts > vi->videopts[1])
1873                             vi->sectpts[1] = finalaudiopts;
1874                         else
1875                             vi->sectpts[1] = vi->videopts[1];
1876                       } /*if*/
1877                   } /*for*/
1878                 if (!complained)
1879                     break;
1880               } /*for*/
1881             // guess at non-video vobus
1882             for (i = 0; i < thisvob->numvobus; i++)
1883               {
1884                 struct vobuinfo * const vi = thisvob->vobu + i;
1885                 if (!vi->hasvideo)
1886                   {
1887                     int j, k;
1888                     pts_t firstaudiopts = -1, p;
1889 
1890                     for (j = 0; j < 32; j++)
1891                       {
1892                         const struct audchannel * const ach = thisvob->audch + j;
1893                         for (k = 0; k < ach->numaudpts; k++)
1894                             if (ach->audpts[k].asect >= vi->sector)
1895                               {
1896                                 if (firstaudiopts == -1 || ach->audpts[k].pts[0] < firstaudiopts)
1897                                     firstaudiopts = ach->audpts[k].pts[0];
1898                                 break;
1899                               } /*if; for*/
1900                       } /*for*/
1901                     if (firstaudiopts == -1)
1902                       {
1903                         fprintf
1904                           (
1905                             stderr,
1906                             "WARN: Cannot detect pts for VOBU at inoffset %#"PRIx64" if there is"
1907                                 " no audio or video\nWARN: Using SCR instead.\n",
1908                             inoffset
1909                           );
1910                         firstaudiopts = readscr(vi->sectdata + 4) + 4 * 147;
1911                           // 147 is roughly the minimum pts that must transpire between packets;
1912                           // we give a couple packets of buffer to allow the dvd player to
1913                           // process the data
1914                       } /*if*/
1915                     if (i)
1916                       {
1917                         pts_t frpts = getframepts(va);
1918                         p = firstaudiopts - thisvob->vobu[i - 1].sectpts[0];
1919                         // ensure this is a multiple of a framerate, just to be nice
1920                         p += frpts - 1;
1921                         p -= p % frpts;
1922                         p += thisvob->vobu[i - 1].sectpts[0];
1923                         if (p < thisvob->vobu[i - 1].sectpts[1])
1924                           {
1925                             fprintf
1926                               (
1927                                 stderr,
1928                                 "ERR:  pts %"PRId64" rounded up to %"PRId64" at rate"
1929                                     " %"PRId64" lies within previous vobu %d"
1930                                     " [%"PRId64"..%"PRId64"] at inoffset %#"PRIx64"\n",
1931                                 firstaudiopts,
1932                                 p,
1933                                 frpts,
1934                                 i - 1,
1935                                 thisvob->vobu[i - 1].sectpts[0],
1936                                 thisvob->vobu[i - 1].sectpts[1],
1937                                 inoffset
1938                               );
1939                             exit(1);
1940                           } /*if*/
1941                         thisvob->vobu[i - 1].sectpts[1] = p;
1942                       }
1943                     else
1944                       {
1945                         fprintf
1946                           (
1947                             stderr,
1948                             "ERR:  Cannot infer pts for VOBU at inoffset %#"PRIx64" if there is"
1949                                 " no audio or video and it is the\nERR:  first VOBU.\n",
1950                             inoffset
1951                           );
1952                         exit(1);
1953                       } /*if*/
1954                     vi->sectpts[0] = p;
1955                     // if we can easily predict the end pts of this sector,
1956                     // then fill it in.  otherwise, let the next iteration do it
1957                     if (i + 1 == thisvob->numvobus)
1958                       { // if this is the end of the vob, use the final audio pts as the last pts
1959                         if( finalaudiopts>vi->sectpts[0] )
1960                             p = finalaudiopts;
1961                         else
1962                             p = vi->sectpts[0] + getframepts(va);
1963                               // add one frame of a buffer, so we don't have a zero (or less) length vobu
1964                       }
1965                     else if (thisvob->vobu[i+1].hasvideo)
1966                       // if the next vobu has video, use the start of the video as the end of this vobu
1967                         p = thisvob->vobu[i + 1].sectpts[0];
1968                     else
1969                       // the next vobu is an audio only vobu, and will backfill the pts as necessary
1970                         continue;
1971                     if (p <= vi->sectpts[0])
1972                       {
1973                         fprintf
1974                           (
1975                             stderr,
1976                             "ERR:  Audio and video are too poorly synchronised at inoffset"
1977                                 " %#"PRIx64"; you must remultiplex.\n",
1978                             inoffset
1979                           );
1980                         exit(1);
1981                       } /*if*/
1982                     vi->sectpts[1] = p;
1983                   } /*if*/
1984               } /*for*/
1985 
1986             fprintf(stderr, "\nINFO: Video pts = ");
1987             printpts(thisvob->vobu[0].videopts[0]);
1988             fprintf(stderr, " .. ");
1989             for (i = thisvob->numvobus - 1; i >= 0; i--)
1990                 if (thisvob->vobu[i].hasvideo)
1991                   {
1992                     printpts(thisvob->vobu[i].videopts[1]);
1993                     break;
1994                   } /*if; for*/
1995             if (i < 0)
1996                 fprintf(stderr, "??");
1997             for (i = 0; i < 64; i++)
1998               {
1999                 const struct audchannel * const ach = &thisvob->audch[i];
2000                 if (ach->numaudpts)
2001                   {
2002                     fprintf(stderr, "\nINFO: Audio[%d] pts = ", i);
2003                     printpts(ach->audpts[0].pts[0]);
2004                     fprintf(stderr, " .. ");
2005                     printpts(ach->audpts[ach->numaudpts - 1].pts[1]);
2006                   } /*if*/
2007               } /*for*/
2008             fprintf(stderr, "\n");
2009           } /*if*/
2010       } /*for*/
2011     writeclose();
2012     printvobustatus(va, cursect, true);
2013     fprintf(stderr, "\n");
2014     free(crs);
2015     return 1;
2016   } /*FindVobus*/
2017 
pabs(pts_t pts)2018 static pts_t pabs(pts_t pts)
2019   /* returns the absolute value of pts. */
2020   {
2021     if (pts < 0)
2022         return -pts;
2023     return pts;
2024   } /*pabs*/
2025 
findnearestvobu(struct vob * va,pts_t pts)2026 static int findnearestvobu(struct vob *va, pts_t pts)
2027   /* returns the index of the VOBU closest in time to pts. */
2028   {
2029     int l = 0, h = va->numvobus - 1, i;
2030     if (h < 0)
2031         return -1;
2032     pts += va->vobu[0].sectpts[0];
2033     i = findvobu(va, pts, l, h);
2034     if
2035       (
2036             i + 1 < va->numvobus
2037         &&
2038             i >= 0
2039         &&
2040             pabs(pts - va->vobu[i + 1].sectpts[0]) < pabs(pts - va->vobu[i].sectpts[0])
2041               /* next one is closer */
2042       )
2043         i++;
2044     return i;
2045   } /*findnearestvobu*/
2046 
MarkChapters(struct vobgroup * va)2047 void MarkChapters(struct vobgroup *va)
2048   /* fills in scellid, ecellid, vobcellid, firstvobuincell, lastvobuincell, numcells fields
2049     to mark all the cells and programs. */
2050   {
2051     int i, j, k, lastvobuid;
2052     // mark start and stop points
2053     lastvobuid = -1;
2054     for (i = 0; i < va->numallpgcs; i++)
2055         for (j = 0; j < va->allpgcs[i]->numsources; j++)
2056           {
2057           /* use vobcellid fields to mark start of cells, and scellid and ecellid fields
2058             to hold vobu indexes, all to be replaced later with right values */
2059             struct source * const thissource = va->allpgcs[i]->sources[j];
2060             for (k = 0; k < thissource->numcells; k++)
2061               {
2062                 int v;
2063                 v = findnearestvobu(thissource->vob, thissource->cells[k].startpts);
2064                 if (v >= 0 && v < thissource->vob->numvobus)
2065                   {
2066                     if (thissource->cells[k].ischapter != CELL_NEITHER) /* from Wolfgang Wershofen */
2067                       { /* info for user corresponding to the points they marked */
2068                         fprintf(stderr, "CHAPTERS: VTS[%d/%d] ", i + 1, j + 1);
2069                         printpts(thissource->vob->vobu[v].sectpts[0] - thissource->vob->vobu[0].sectpts[0]);
2070                         fprintf(stderr, "\n");
2071                       } /*if*/
2072                     thissource->vob->vobu[v].vobcellid = 1; /* cell starts here */
2073                   } /*if*/
2074                 thissource->cells[k].scellid = v; /* cell starts here */
2075                 if
2076                   (
2077                         lastvobuid != v
2078                     &&
2079                         thissource->vob->vobu[v].firstIfield != 0
2080                   )
2081                   {
2082                     fprintf
2083                       (
2084                         stderr,
2085                         "WARN: GOP may not be closed on cell %d of source %s of pgc %d\n",
2086                         k + 1,
2087                         thissource->fname,
2088                         i + 1
2089                       );
2090                   } /*if*/
2091                 if (thissource->cells[k].endpts >= 0)
2092                   {
2093                     v = findnearestvobu(thissource->vob, thissource->cells[k].endpts);
2094                     if (v >= 0 && v < thissource->vob->numvobus)
2095                         thissource->vob->vobu[v].vobcellid = 1; /* another cell should start here */
2096                   }
2097                 else /* -ve end time => cell goes to end of vob */
2098                     v = thissource->vob->numvobus;
2099                 thissource->cells[k].ecellid = v; /* next cell starts here */
2100                 lastvobuid = v;
2101               } /*for*/
2102           } /*for; for*/
2103    /* At this point, the vobcellid fields have been set to 1 for all VOBUs which
2104     are supposed to start new cells. */
2105     for (i = 0; i < va->numvobs; i++)
2106       {
2107       /* fill in the vobcellid fields with the right values, and also
2108         firstvobuincell and lastvobuincell */
2109         int cellvobu = 0;
2110         int cellid = 0;
2111         va->vobs[i]->vobu[0].vobcellid = 1; /* ensure first vobu starts a cell */
2112         for (j = 0; j < va->vobs[i]->numvobus; j++)
2113           {
2114             struct vobuinfo * const thisvobu = &va->vobs[i]->vobu[j];
2115             if (thisvobu->vobcellid)
2116               {
2117                 cellid++; /* start new cell */
2118                 cellvobu = j; /* this VOBU is first in cell */
2119               } /*if*/
2120             thisvobu->vobcellid = cellid + va->vobs[i]->vobid * 256;
2121             thisvobu->firstvobuincell = cellvobu;
2122           } /*for*/
2123         cellvobu = va->vobs[i]->numvobus - 1;
2124         for (j = cellvobu; j >= 0; j--)
2125           { /* fill in the lastvobuincell fields */
2126             struct vobuinfo * const thisvobu = &va->vobs[i]->vobu[j];
2127             thisvobu->lastvobuincell = cellvobu;
2128             if (thisvobu->firstvobuincell == j) /* reached start of this cell */
2129                 cellvobu = j - 1;
2130           } /*for*/
2131         va->vobs[i]->numcells = cellid;
2132         if (cellid >= 256)
2133           {
2134             fprintf
2135               (
2136                 stderr,
2137                 "ERR:  VOB %s has too many cells (%d, 256 allowed)\n",
2138                 va->vobs[i]->fname,
2139                 cellid
2140               );
2141             exit(1);
2142           } /*if*/
2143       } /*for*/
2144   /* Now fill in right values for scellid and ecellid fields, replacing the
2145     vobu indexes I previously put in with the corresponding vobcellid values I
2146     have just computed. */
2147     for (i = 0; i < va->numallpgcs; i++)
2148         for (j = 0; j < va->allpgcs[i]->numsources; j++)
2149           {
2150             struct source * const thissource = va->allpgcs[i]->sources[j];
2151             for (k = 0; k < thissource->numcells; k++)
2152               {
2153                 struct cell * const thiscell = &thissource->cells[k];
2154                 if (thiscell->scellid < 0)
2155                     thiscell->scellid = 1;
2156                 else if (thiscell->scellid < thissource->vob->numvobus)
2157                     thiscell->scellid = thissource->vob->vobu[thiscell->scellid].vobcellid & 255;
2158                 else
2159                     thiscell->scellid = thissource->vob->numcells + 1;
2160                 if (thiscell->ecellid < 0)
2161                     thiscell->ecellid = 1;
2162                 else if (thiscell->ecellid < thissource->vob->numvobus)
2163                     thiscell->ecellid = thissource->vob->vobu[thiscell->ecellid].vobcellid & 255;
2164                 else
2165                     thiscell->ecellid = thissource->vob->numcells + 1;
2166               /* near as I can tell, ecellid will either be equal to scellid or 1 greater */
2167                 va->allpgcs[i]->numcells += thiscell->ecellid - thiscell->scellid;
2168                 if (thiscell->scellid != thiscell->ecellid && thiscell->ischapter != CELL_NEITHER)
2169                   {
2170                     va->allpgcs[i]->numprograms++; /* ischapter is CELL_PROGRAM or CELL_CHAPTER_PROGRAM */
2171                     if (thiscell->ischapter == CELL_CHAPTER_PROGRAM)
2172                         va->allpgcs[i]->numchapters++;
2173                     if (va->allpgcs[i]->numprograms >= 256)
2174                       {
2175                         fprintf
2176                           (
2177                             stderr,
2178                             "ERR:  PGC %d has too many programs (%d, 256 allowed)\n",
2179                             i + 1,
2180                             va->allpgcs[i]->numprograms
2181                           );
2182                         exit(1);
2183                       } /*if*/
2184                     // if numprograms<256, then numchapters<256, so
2185                     // no need to doublecheck
2186                   } /*if*/
2187               } /*for*/
2188           } /*for; for*/
2189   } /*MarkChapters*/
2190 
getcellaudiopts(const struct vobgroup * va,int vcid,int ach,int w)2191 static pts_t getcellaudiopts(const struct vobgroup *va,int vcid,int ach,int w)
2192 {
2193     const struct vob *v=va->vobs[(vcid>>8)-1];
2194     const struct audchannel *a=&v->audch[ach];
2195     int ai=0;
2196 
2197     assert((vcid&255)==(w?v->numcells:1));
2198     if( w )
2199         ai=a->numaudpts-1;
2200     return a->audpts[ai].pts[w];
2201 }
2202 
hasaudio(const struct vobgroup * va,int vcid,int ach,int w)2203 static int hasaudio(const struct vobgroup *va,int vcid,int ach,int w)
2204 {
2205     const struct vob *v=va->vobs[(vcid>>8)-1];
2206     const struct audchannel *a=&v->audch[ach];
2207 
2208     assert((vcid&255)==(w?v->numcells:1));
2209 
2210     return a->numaudpts!=0;
2211 }
2212 
getcellvideopts(const struct vobgroup * va,int vcid,int w)2213 static pts_t getcellvideopts(const struct vobgroup *va,int vcid,int w)
2214 {
2215     const struct vob *v=va->vobs[(vcid>>8)-1];
2216     int vi=0;
2217 
2218     assert((vcid&255)==(w?v->numcells:1));
2219     if( w )
2220         vi=v->numvobus-1;
2221     // we use sectpts instead of videopts because sometimes you will
2222     // present the last video frame for a long time; we want to know
2223     // the last presented time stamp: sectpts
2224     return v->vobu[vi].sectpts[w];
2225 }
2226 
calcaudiodiff(const struct vobgroup * va,int vcid,int ach,int w)2227 static pts_t calcaudiodiff(const struct vobgroup *va,int vcid,int ach,int w)
2228 {
2229     return getcellvideopts(va,vcid,w)-getcellaudiopts(va,vcid,ach,w);
2230 }
2231 
calcaudiogap(const struct vobgroup * va,int vcid0,int vcid1,int ach)2232 int calcaudiogap(const struct vobgroup *va,int vcid0,int vcid1,int ach)
2233 {
2234     if( vcid0==-1 || vcid1==-1 )
2235         return 0;
2236     if( vcid1==vcid0+1 )
2237         return 0;
2238     if( (vcid1&255)==1 && va->vobs[(vcid0>>8)-1]->numcells==(vcid0&255) ) {
2239         int g1,g2;
2240 
2241         // there is no discontinuity if there is no audio in the second half
2242         if( !hasaudio(va,vcid1,ach,0) )
2243             return 0;
2244 
2245         // we have problems if the second half has audio but the first doesn't
2246         if( !hasaudio(va,vcid0,ach,1) && hasaudio(va,vcid1,ach,0) ) {
2247             fprintf(stderr,"WARN: Transition from non-audio to audio VOB; assuming discontinuity.\n");
2248             return 1;
2249         }
2250 
2251         g1=calcaudiodiff(va,vcid0,ach,1);
2252         g2=calcaudiodiff(va,vcid1,ach,0);
2253         return g1!=g2;
2254     }
2255     fprintf(stderr,"WARN: Do not know how to compute the audio gap between '%s' and '%s', assuming discontinuity.\n",va->vobs[(vcid0>>8)-1]->fname,va->vobs[(vcid1>>8)-1]->fname);
2256     return 1;
2257 }
2258 
FixVobus(const char * fbase,const struct vobgroup * va,const struct workset * ws,vtypes ismenu)2259 void FixVobus(const char *fbase,const struct vobgroup *va,const struct workset *ws,vtypes ismenu)
2260   /* fills in the NAV packs (i.e. PCI and DSI packets) for each VOBU in the
2261     already-written output VOB files. */
2262   {
2263     int outvob = -1;
2264     int vobuindex, j, pn, fnum = -2;
2265     pts_t scr;
2266     int vff, vrew;
2267     int totvob, curvob; /* for displaying statistics */
2268 
2269     totvob = 0;
2270     for (pn = 0; pn < va->numvobs; pn++)
2271         totvob += va->vobs[pn]->numvobus;
2272     curvob = 0;
2273 
2274     for (pn = 0; pn < va->numvobs; pn++)
2275       {
2276         const struct vob * const thisvob = va->vobs[pn];
2277         for (vobuindex = 0; vobuindex < thisvob->numvobus; vobuindex++)
2278           {
2279             const struct vobuinfo * const thisvobu = &thisvob->vobu[vobuindex];
2280             static unsigned char buf[2048];
2281 
2282             if (thisvobu->fnum != fnum)
2283               {
2284               /* time to start a new output file */
2285                 if (outvob >= 0)
2286                     flushclose(outvob);
2287                 fnum = thisvobu->fnum;
2288                 if (fbase)
2289                   {
2290                     char * fname;
2291                     if (fnum == -1)
2292                       {
2293                         fname = strdup(fbase);
2294                       }
2295                     else
2296                       {
2297                         fname = sprintf_alloc("%s_%d.VOB", fbase, fnum);
2298                       } /*if*/
2299                     outvob = open(fname, O_WRONLY | O_BINARY);
2300                     if (outvob < 0)
2301                       {
2302                         fprintf
2303                           (
2304                             stderr,
2305                             "\nERR:  Error %d opening %s: %s\n",
2306                             errno,
2307                             fname,
2308                             strerror(errno)
2309                           );
2310                         exit(1);
2311                       } /*if*/
2312                     free(fname);
2313                   } /*if*/
2314               } /*if*/
2315 
2316             memcpy(buf, thisvobu->sectdata, 0x26);
2317             write4(buf + 0x26, 0x100 + MPID_PRIVATE2); // private stream 2
2318             write2(buf + 0x2a, 0x3d4); // length
2319             buf[0x2c] = 0; /* substream ID, 0 = PCI */
2320             memset(buf + 0x2d,0, 0x400 - 0x2d);
2321             write4(buf + 0x400, 0x100 + MPID_PRIVATE2); // private stream 2
2322             write2(buf + 0x404, 0x3fa); // length
2323             buf[0x406] = 1; /* substream ID, 1 = DSI */
2324             memset(buf + 0x407, 0, 0x7ff - 0x407);
2325 
2326             scr = readscr(buf + 4);
2327 
2328             write4(buf + 0x2d, thisvobu->sector); /* sector number of this block */
2329           /* buf[0x35 .. 0x38] -- prohibited user ops -- none for now */
2330             write4(buf + 0x39, thisvobu->sectpts[0]); /* start presentation time (vobu_s_ptm) */
2331             write4(buf + 0x3d, thisvobu->sectpts[1]); /* end presentation time (vobu_e_ptm) */
2332             if (thisvobu->hasseqend) // if sequence_end_code
2333                 write4(buf + 0x41, thisvobu->videopts[1]); // vobu_se_e_ptm
2334             write4
2335               (
2336                 buf + 0x45,
2337                 buildtimeeven
2338                   (
2339                     va,
2340                     thisvobu->sectpts[0] - thisvob->vobu[thisvobu->firstvobuincell].sectpts[0]
2341                   ) // total guess
2342               );
2343               /* c_eltm -- BCD cell elapsed time + frame rate */
2344 
2345             if (thisvob->progchain->numbuttons)
2346               {
2347               /* fill in PCI packet with button info */
2348                 const struct pgc * const pg = thisvob->progchain;
2349                 int mask = getsubpmask(&va->vd), nrgrps, grp;
2350                 char idmap[3];
2351 
2352                 write2(buf + 0x8d, 1); /* highlight status = all new highlight information for this VOBU */
2353                 write4(buf + 0x8f, thisvob->vobu[0].sectpts[0]); /* highlight start time */
2354                 write4(buf + 0x93, -1); /* highlight end time */
2355                 write4(buf + 0x97, -1); /* button selection end time (ignore user after this) */
2356 
2357                 nrgrps = 0;
2358                 write2(buf + 0x9b, 0); /* button groupings, none to begin with */
2359                 for (j = 0; j < 4; j++)
2360                     if (mask & (1 << j))
2361                       {
2362                         assert(nrgrps < 3);
2363                         idmap[nrgrps] = j;
2364                         write2
2365                           (
2366                             buf + 0x9b,
2367                                 read2(buf + 0x9b)
2368                             +
2369                                 0x1000 /* add another button group */
2370                             +
2371                                 (
2372                                     ((1 << j) >> 1)
2373                                       /* panscan/letterbox/normal bit for widescreen,
2374                                         0 for narrowscreen */
2375                                 <<
2376                                     (2 - nrgrps) * 4
2377                                       /* bit-shifted into appropriate button group nibble */
2378                                 )
2379                           );
2380                         nrgrps++;
2381                       } /*if; for*/
2382                 assert(nrgrps > 0);
2383 
2384                 buf[0x9e] = pg->numbuttons; /* number of buttons */
2385                 buf[0x9f] = pg->numbuttons; /* number of numerically-selected buttons */
2386                 memcpy(buf + 0xa3, thisvob->buttoncoli, 24);
2387                 for (grp = 0; grp < nrgrps; grp++)
2388                   {
2389                     unsigned char *boffs = buf + 0xbb + 18 * (grp * 36 / nrgrps);
2390                       /* divide BTN_IT entries equally among all groups -- does this matter? */
2391                     const int sid = pg->subpmap[0][(int)idmap[grp]] & 127;
2392 
2393                     for (j = 0; j < pg->numbuttons; j++)
2394                       /* fixme: no check against overrunning allocated portion of BTN_IT array? */
2395                       {
2396                         static unsigned char compilebuf[128 * 8], *rbuf;
2397                         const struct button * const b = pg->buttons + j;
2398                         const struct buttoninfo *bi;
2399                         int k;
2400 
2401                         for (k = 0; k < b->numstream; k++)
2402                             if (b->stream[k].substreamid == sid)
2403                                 break;
2404                         if (k == b->numstream)
2405                             continue; /* no matching button def for this substream */
2406                         bi = &b->stream[k];
2407 
2408                       /* construct BTN_IT -- Button Information Table entry */
2409                         boffs[0] = (bi->grp * 64) | (bi->x1 >> 4);
2410                         boffs[1] = (bi->x1 << 4) | (bi->x2 >> 8);
2411                         boffs[2] = bi->x2;
2412                         boffs[3] = (bi->autoaction ? 64 : 0) | (bi->y1 >> 4);
2413                         boffs[4] = (bi->y1 << 4) | (bi->y2 >> 8);
2414                         boffs[5] = bi->y2;
2415                         boffs[6] = findbutton(pg, bi->up, (j == 0) ? pg->numbuttons : j);
2416                         boffs[7] = findbutton(pg, bi->down, (j + 1 == pg->numbuttons) ? 1 : j + 2);
2417                         boffs[8] = findbutton(pg, bi->left, (j == 0) ? pg->numbuttons : j);
2418                         boffs[9] = findbutton(pg, bi->right, (j + 1 == pg->numbuttons) ? 1 : j + 2);
2419                         rbuf = vm_compile(compilebuf, compilebuf, ws, pg->pgcgroup, pg, b->commands, ismenu);
2420                         if (rbuf - compilebuf == 8)
2421                           {
2422                             memcpy(boffs + 10, compilebuf, 8);
2423                           }
2424                         else if (allowallreg)
2425                           {
2426                             fprintf
2427                               (
2428                                 stderr,
2429                                 "ERR:  Button command is too complex to fit in one instruction,"
2430                                     " and allgprm==true.\n"
2431                               );
2432                             exit(1);
2433                           }
2434                         else
2435                             write8(boffs + 10, 0x71, 0x01, 0x00, 0x0F, 0x00, j + 1, 0x00, 0x0d);
2436                               // g[15] = j && linktailpgc
2437                               /* transfer to full instruction sequence which will be
2438                                 generated by dvdpgc.c:genpgc */
2439                         boffs += 18;
2440                       } /*for j*/
2441                   } /*for grp*/
2442               } /* if thisvob->progchain->numbuttons */
2443 
2444           /* fill in DSI packet */
2445             write4(buf + 0x407, scr);
2446             write4(buf + 0x40b, thisvobu->sector); // current lbn
2447             if (thisvobu->numref > 0)
2448               {
2449               /* up to three reference frame relative end blocks */
2450                 for (j = 0; j < thisvobu->numref; j++)
2451                     write4(buf + 0x413 + j * 4, thisvobu->lastrefsect[j] - thisvobu->sector);
2452                 for (; j < 3; j++) /* duplicate last one if less than 3 */
2453                     write4(buf + 0x413 + j * 4, thisvobu->lastrefsect[thisvobu->numref - 1] - thisvobu->sector);
2454               } /*if*/
2455             write2(buf + 0x41f, thisvobu->vobcellid >> 8); /* VOB number */
2456             buf[0x422] = thisvobu->vobcellid; /* cell number within VOB */
2457             write4(buf + 0x423, read4(buf + 0x45)); /* cell elapsed time, BCD + frame rate */
2458           /* interleaved unit stuff not supported for now */
2459             write4(buf + 0x433, thisvob->vobu[0].sectpts[0]);
2460               /* time of first video frame in first GOP of VOB */
2461             write4(buf + 0x437, thisvob->vobu[thisvob->numvobus - 1].sectpts[1]);
2462               /* time of last video frame in last GOP of VOB */
2463           /* audio gap stuff not supported for now */
2464           /* seamless angle stuff not supported for now */
2465             write4
2466               (
2467                 buf + 0x4f1,
2468                 getsect(thisvob, vobuindex, findnextvideo(thisvob, vobuindex, 1), 0, 0xbfffffff)
2469               );
2470               /* offset to next VOBU with video */
2471           /* offset to next VOBU at various times forward filled in below */
2472             write4(buf + 0x541, getsect(thisvob, vobuindex, vobuindex + 1, 0, 0x3fffffff));
2473               /* offset to next VOBU */
2474             write4(buf + 0x545, getsect(thisvob, vobuindex, vobuindex - 1, 0, 0x3fffffff));
2475               /* offset to previous VOBU */
2476           /* offset to previous VOBU at various times backward filled in below */
2477             write4
2478               (
2479                 buf + 0x595,
2480                 getsect(thisvob, vobuindex, findnextvideo(thisvob, vobuindex, -1), 0, 0xbfffffff)
2481               );
2482               /* offset to previous VOBU with video */
2483             for (j = 0; j < va->numaudiotracks; j++)
2484               {
2485                 int s = getaudch(va, j);
2486                 if (s >= 0)
2487                     s = findaudsect(thisvob, s, thisvobu->sectpts[0], thisvobu->sectpts[1]);
2488                 if (s >= 0)
2489                   {
2490                     s = s - thisvobu->sector;
2491                     if (s > 0x1fff || s < -(0x1fff))
2492                       {
2493                         fprintf
2494                           (
2495                             stderr,
2496                             "\nWARN: audio sector out of range: %d (vobu #%d, pts ",
2497                             s,
2498                             vobuindex
2499                           );
2500                         printpts(thisvobu->sectpts[0]);
2501                         fprintf(stderr, ")\n");
2502                         s = 0;
2503                       } /*if*/
2504                     if (s < 0)
2505                         s = (-s) | 0x8000; /* absolute value + backward-direction flag */
2506                   }
2507                 else
2508                     s = 0x3fff; /* no more audio for this stream */
2509                 write2(buf + 0x599 + j * 2, s);
2510                   /* relative offset to first audio packet in this stream for this VOBU */
2511               } /*for*/
2512             for (j = 0; j < va->numsubpicturetracks; j++)
2513               {
2514                 const struct audchannel * const ach = &thisvob->audch[j | 32];
2515                 int s;
2516                 if (ach->numaudpts)
2517                   {
2518                     int id = findspuidx(thisvob, j | 32, thisvobu->sectpts[0]);
2519                     // if overlaps A, point to A
2520                     // else if (A before here or doesn't exist) and (B after here or doesn't exist),
2521                     //     point to here
2522                     // else point to B
2523                     if
2524                       (
2525                             id >= 0
2526                         &&
2527                             ach->audpts[id].pts[0] < thisvobu->sectpts[1]
2528                         &&
2529                             ach->audpts[id].pts[1] >= thisvobu->sectpts[0]
2530                       )
2531                         s = findvobubysect(thisvob, ach->audpts[id].asect);
2532                     else if
2533                       (
2534                             (id < 0 || ach->audpts[id].pts[1] < thisvobu->sectpts[0])
2535                          &&
2536                             (
2537                                 id + 1 == ach->numaudpts
2538                             ||
2539                                 ach->audpts[id + 1].pts[0] >= thisvobu->sectpts[1]
2540                             )
2541                       )
2542                         s = vobuindex;
2543                     else
2544                         s = findvobubysect(thisvob, ach->audpts[id + 1].asect);
2545                     id = (s < vobuindex);
2546                     s = getsect(thisvob, vobuindex, s, 0, 0x7fffffff) & 0x7fffffff;
2547                     if (!s) /* same VOBU */
2548                         s = 0x7fffffff;
2549                           /* indicates current or later VOBU, no explicit forward offsets */
2550                     if (s != 0x7fffffff && id)
2551                         s |= 0x80000000; /* indicates offset to prior VOBU */
2552                   }
2553                 else
2554                     s = 0; /* doesn't exist */
2555                 write4(buf + 0x5a9 + j * 4, s);
2556                   /* relative offset to VOBU (NAV pack) containing subpicture data
2557                     for this stream for this VOBU */
2558               } /*for*/
2559             write4(buf + 0x40f, thisvobu->lastsector - thisvobu->sector);
2560               /* relative offset to last sector of VOBU */
2561             vff = vobuindex;
2562             vrew = vobuindex;
2563             for (j = 0; j < 19; j++)
2564               {
2565               /* fill in offsets to next/previous VOBUs at various time steps */
2566                 int nff, nrew;
2567                 nff = findvobu
2568                   (
2569                     thisvob,
2570                     thisvobu->sectpts[0] + timeline[j] * DVD_FFREW_HALFSEC,
2571                     thisvobu->firstvobuincell,
2572                     thisvobu->lastvobuincell
2573                   );
2574                 // a hack -- the last vobu in the cell shouldn't have any forward ptrs
2575                 // EXCEPT this hack violates both Grosse Pointe Blank and Bullitt -- what was I thinking?
2576                 // if (i == thisvobu->lastvobuincell)
2577                 //      nff = i + 1;
2578                 nrew = findvobu
2579                   (
2580                     thisvob,
2581                     thisvobu->sectpts[0] - timeline[j] * DVD_FFREW_HALFSEC,
2582                     thisvobu->firstvobuincell,
2583                     thisvobu->lastvobuincell
2584                   );
2585               /* note table entries are in order of decreasing time step */
2586                 write4
2587                   (
2588                     buf + 0x53d - j * 4, /* forward jump */
2589                     getsect(thisvob, vobuindex, nff, j >= 15 && nff > vff + 1, 0x3fffffff)
2590                   );
2591                 write4
2592                   (
2593                     buf + 0x549 + j * 4, /* backward jump */
2594                     getsect(thisvob, vobuindex, nrew, j >= 15 && nrew < vrew - 1, 0x3fffffff)
2595                   );
2596                 vff = nff;
2597                 vrew = nrew;
2598               } /*for*/
2599 
2600           /* NAV pack all done, write it out */
2601             if (outvob != -1)
2602               {
2603                 if (lseek(outvob, thisvobu->fsect * 2048, SEEK_SET) == (off_t)-1)
2604                   /* where the NAV pack should go */
2605                   {
2606                     fprintf(stderr, "ERR:  Error %d -- %s -- seeking in output VOB\n", errno, strerror(errno));
2607                     exit(1);
2608                   } /*if*/
2609                 if (write(outvob, buf, 2048) != 2048)
2610                   {
2611                     fprintf
2612                       (
2613                         stderr,
2614                         "ERR:  Error %d -- %s -- writing NAV PACK to output VOB\n",
2615                         errno,
2616                         strerror(errno)
2617                       );
2618                     exit(1);
2619                   } /*if*/
2620                   /* update it */
2621               } /*if*/
2622             curvob++;
2623             if (!(curvob & 15)) /* time for another progress update */
2624                 fprintf
2625                   (
2626                     stderr,
2627                     "STAT: fixing VOBU at %dMB (%d/%d, %d%%)\r",
2628                     thisvobu->sector / 512,
2629                     curvob + 1,
2630                     totvob,
2631                     curvob * 100 / totvob
2632                   );
2633           } /*for vobuindex*/
2634       } /*for pn*/
2635     if (outvob != -1)
2636         flushclose(outvob);
2637     if (totvob > 0)
2638         fprintf(stderr, "STAT: fixed %d VOBUs                         ", totvob);
2639     fprintf(stderr, "\n");
2640   } /*FixVobus*/
2641