1 /*
2     spuunmux mainline
3 */
4 /*
5  * Copyright (C) 2002, 2003 Jan Panteltje <panteltje@yahoo.com>,
6  *
7  * Modified by Zachary Brewster-Geisz, 2003, to work on big-endian
8  * machines.
9  *
10  * Modified by Henry Mason, 2003, to use both PNG and BMP, and to use
11  * the dvdauthor submux format.
12  *
13  * Modified and copy right Jan Panteltje 2002
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or (at
17  * your option) any later version.
18  *
19  * With many changes by Scott Smith (trckjunky@users.sourceforge.net)
20  *
21  * Svcd decoding by Giacomo Comes <encode2mpeg@users.sourceforge.net>
22  *
23  * This program is distributed in the hope that it will be useful, but
24  * WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
26  * General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program; if not, write to the Free Software
30  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
31  * MA 02110-1301 USA.
32  */
33 
34 #include "compat.h"
35 
36 #include <fcntl.h>
37 #include <errno.h>
38 
39 #include <netinet/in.h>
40 
41 #include <png.h>
42 #include <zlib.h>
43 
44 #include "rgb.h"
45 #include "common.h"
46 #include "conffile.h"
47 
48 #define CBUFSIZE 65536 /* big enough for any MPEG packet */
49 #define PSBUFSIZE 10
50 
51 static unsigned int add_offset;
52 
53 static int debug = 0;
54 
55 static int video_format = VF_NONE;
56 static bool full_size = false;
57 static unsigned int pts, spts, subi, subs, subno;
58 static int ofs, ofs1;
59   /* offsets from beginning of SPU to bottom and top field data set by last SPU_SET_DSPXA command */
60 static unsigned char sub[65536];
61 static unsigned char next_bits;
62 static const char *base_name;
63 static unsigned int have_bits;
64 static FILE *fdo;
65 static unsigned char svcd_adjust;
66 
67 static colorspec
68     current_palette[16]; /* current PGC colour table, alpha unused */
69 
70 struct spu /* data for one subpicture unit (SPU) */
71   {
72     unsigned char *img; /* image data */
73     unsigned int x0, y0, xd, yd; /* display bounds */
74     unsigned int pts[2]; /* start time, end time */
75     unsigned int subno; /* index used for generating unique filenames */
76     unsigned int force_display;
77     unsigned int nummap; /* length of map array */
78     struct colormap *map;
79       /* array where entry 0 is for colours as specified in SPU, other entries
80         are for button colours as specified in PGC, so if they overlap, the
81         last entry takes precedence */
82     struct spu *next; /* linked list */
83   };
84 
85 static struct spu
86     *pending_spus = 0;
87 
88 struct button /* information about a button */
89   {
90     char *name;
91     bool autoaction;
92     int x1, y1, x2, y2;
93     char *up, *down, *left, *right; /* names of neighbouring buttons */
94     int grp; /* which group button belongs to */
95   };
96 
97 struct dispdetails /* information about button grouping */
98   {
99     int pts[2]; /* start time, end time */
100     int numpal; /* nr used entries in palette */
101     uint32_t palette[16]; /* RGB colours */
102     int numcoli; /* nr of SL_COLI entries present, not checked! */
103     uint32_t coli[6]; /* up to 3 8-byte SL_COLI entries */
104     int numbuttons; /* length of buttons array */
105     struct button *buttons; /* array */
106     struct dispdetails *next; /* linked list */
107   };
108 
109 static struct dispdetails
110     *pending_buttons = 0;
111 
112 struct colormap /* for determining which colours take precedence in overlapping areas */
113   {
114     uint16_t color; /* four 4-bit colour indexes */
115     uint16_t contrast; /* four 4-bit contrast (transparency) values */
116     int x1, y1, x2, y2; /* bounds of area over which entries are valid */
117   };
118 
read4(const unsigned char * p)119 static unsigned int read4(const unsigned char *p)
120   /* decodes 4 bytes as a big-endian integer starting at p. */
121   {
122     return
123             p[0] << 24
124         |
125             p[1] << 16
126         |
127             p[2] << 8
128         |
129             p[3];
130   } /*read4*/
131 
read2(const unsigned char * p)132 static unsigned int read2(const unsigned char *p)
133   /* decodes 2 bytes as a big-endian integer starting at p. */
134   {
135     return
136             p[0] << 8
137         |
138             p[1];
139   } /*read2*/
140 
readpstr(const unsigned char * b,int * i)141 static char *readpstr(const unsigned char *b, int *i)
142   /* extracts a null-terminated string beginning at b[*i], advances *i past it and returns
143     a copy of the string. */
144   {
145     char * const s = strdup((const char *)b + i[0]);
146     i[0] += strlen(s) + 1;
147     return s;
148   } /*readpstr*/
149 
get_next_bits()150 static unsigned char get_next_bits()
151   /* returns next nibble from sub at offset ofs. */
152   {
153     if (!have_bits)
154       {
155         next_bits = sub[ofs++];
156         have_bits = true;
157         return next_bits >> 4;
158       } /*if*/
159     have_bits = false;
160     return next_bits & 15;
161   } /*get_next_bits*/
162 
get_next_svcdbits()163 static unsigned char get_next_svcdbits()
164   /* returns next two bits from sub at offset ofs. */
165   {
166     switch (have_bits)
167       {
168     case 0:
169         ++have_bits;
170         return sub[++ofs] >> 6;
171     break;
172     case 1:
173         ++have_bits;
174         return (sub[ofs] & 0x30) >> 4;
175     break;
176     case 2:
177         ++have_bits;
178         return (sub[ofs] & 0x0c) >> 2;
179     break;
180     default:
181         have_bits = 0;
182         return sub[ofs] & 0x03;
183     break;
184       } /*switch*/
185   } /*get_next_svcdbits*/
186 
getpts(const unsigned char * buf)187 static unsigned int getpts(const unsigned char *buf)
188   /* decodes a presentation time stamp (PTS) beginning at location buf. */
189   {
190     if
191       (
192             !(buf[1] & 0xc0)
193         ||
194             buf[2] < 4
195         ||
196             (buf[3] & 0xe1) != 0x21
197         ||
198             (buf[5] & 1) != 1
199         ||
200             (buf[7] & 1) != 1
201       )
202         return -1; /* doesn't look like a proper PTS */
203     return
204             (buf[7] >> 1)
205         +
206             ((unsigned int)buf[6] << 7)
207         +
208             (((unsigned int)buf[5] & 254) << 14)
209         +
210             ((unsigned int)buf[4] << 22)
211         +
212             (((unsigned int)buf[3] & 14) << 29);
213   } /*getpts*/
214 
addspu(struct spu * s)215 static void addspu(struct spu *s)
216   /* appends s onto pending_spus. */
217   {
218     struct spu **f = &pending_spus;
219     while (*f)
220         f = &f[0]->next;
221     *f = s;
222   } /*addspu*/
223 
add_pending_buttons(struct dispdetails * d)224 static void add_pending_buttons(struct dispdetails *d)
225   /* appends d onto pending_buttons. */
226   {
227     struct dispdetails **dp = &pending_buttons;
228     while (*dp)
229         dp = &dp[0]->next;
230     *dp = d;
231   } /*add_pending_buttons*/
232 
dvddecode()233 static int dvddecode()
234   /* decodes DVD-Video subpicture data from sub and appends a new entry containing
235     the results onto pending_spus. */
236   {
237     unsigned int io;
238     uint16_t total_spu_size, dsize, thiscmdoffset, nextcmdoffset, i, x, y, t;
239     unsigned char c;
240     struct spu *newspu;
241     total_spu_size = read2(sub); /* SPDSZ = size of SPU */
242     dsize = read2(sub + 2); /* SP_DCSQTA = offset to SP_DCSQT */
243     ofs = -1;
244     if (debug > 1)
245         fprintf(stderr, "packet: %d bytes, first block offset=%d\n", total_spu_size, dsize);
246     newspu = malloc(sizeof(struct spu));
247     memset(newspu, 0, sizeof(struct spu));
248     newspu->subno = subno++;
249     newspu->pts[0] = newspu->pts[1] = -1;
250     newspu->nummap = 1;
251     newspu->map = malloc(sizeof(struct colormap));
252     memset(newspu->map, 0, sizeof(struct colormap));
253     newspu->map[0].x2 = 0x7fffffff;
254     newspu->map[0].y2 = 0x7fffffff;
255     i = dsize + 4; /* start of commands */
256     thiscmdoffset = dsize;
257     nextcmdoffset = read2(sub + thiscmdoffset + 2);
258     if (nextcmdoffset < dsize)
259       {
260         if (debug > 0)
261           {
262             fprintf
263               (
264                 stderr,
265                 "invalid control header nextcommand=%d dsize=%d!\n",
266                 nextcmdoffset,
267                 dsize
268               );
269           } /*if*/
270         nextcmdoffset = thiscmdoffset;
271       } /*if*/
272     t = read2(sub + dsize); /* SP_DCSQ_STM = delay in 90kHz units / 1024 before executing commands */
273     if (debug > 2)
274         fprintf
275           (
276             stderr,
277             "\tBLK(%5d): time offset: %d; next: %d\n",
278             dsize, t, read2(sub + dsize + 2)
279           );
280   /* decode the commands, including finding out where the image data is */
281     while (i < total_spu_size)
282       {
283         c = sub[i]; /* get next command */
284         switch (c)
285           {
286         case SPU_FSTA_DSP:
287             if (debug > 4)
288                 fprintf(stderr, "\tcmd(%5d): force start display\n", i);
289             newspu->force_display = true;
290         // fall through
291         case SPU_STA_DSP:
292             if (debug > 4 && c == SPU_STA_DSP)
293                 fprintf(stderr, "\tcmd(%5d): start display\n", i);
294             i++;
295             newspu->pts[0] = t * 1024 + spts;
296         break;
297 
298         case SPU_STP_DSP:
299             if (debug > 4)
300                 fprintf(stderr, "\tcmd(%5d): end display\n", i);
301             newspu->pts[1] = t * 1024 + spts;
302             i++;
303         break;
304 
305         case SPU_SET_COLOR:
306             if (debug > 4)
307                 fprintf(stderr, "\tcmd(%5d): palette=%02x%02x\n", i, sub[i + 1], sub[i + 2]);
308             newspu->map[0].color = read2(sub + i + 1);
309             i += 3;
310         break;
311 
312         case SPU_SET_CONTR:
313             if (debug > 4)
314                 fprintf(stderr, "\tcmd(%5d): transparency=%02x%02x\n", i, sub[i + 1], sub[i + 2]);
315             newspu->map[0].contrast = read2(sub + i + 1);
316             i += 3;
317         break;
318 
319         case SPU_SET_DAREA:
320             newspu->x0 = ((((unsigned int)sub[i + 1]) << 4) + (sub[i + 2] >> 4));
321             newspu->xd = (((sub[i + 2] & 0x0f) << 8) + sub[i + 3]) - newspu->x0 + 1;
322             newspu->y0 = ((((unsigned int)sub[i + 4]) << 4) + (sub[i + 5] >> 4));
323             newspu->yd = (((sub[i + 5] & 0x0f) << 8) + sub[i + 6]) - newspu->y0 + 1;
324             if (debug > 4)
325                 fprintf
326                   (
327                     stderr,
328                     "\tcmd(%5d): image corner=%d,%d, size=%d,%d\n",
329                     i, newspu->x0, newspu->y0, newspu->xd, newspu->yd
330                   );
331             i += 7;
332         break;
333 
334         case SPU_SET_DSPXA:
335             if (ofs >= 0)
336                 fprintf(stderr, "WARN: image pointer already supplied for this subpicture\n");
337                   /* not necessarily wrong, it's just I can't handle it */
338             ofs = read2(sub + i + 1); /* offset to top field data */
339             ofs1 = read2(sub + i + 3); /* offset to bottom field data */
340             if (debug > 4)
341                 fprintf(stderr, "\tcmd(%5d): image offsets=%d,%d\n", i, ofs, ofs1);
342             i += 5;
343         break;
344 
345         case SPU_CMD_END:
346             if (thiscmdoffset == nextcmdoffset) /* no next SP_DCSQ */
347               {
348                 if (debug > 4)
349                   {
350                     fprintf(stderr, "cmd: last end command\n");
351                     if (i + 1 < total_spu_size)
352                       {
353                         fprintf
354                           (
355                             stderr,
356                             "data present after last command (%d bytes, size=%d)\n",
357                             total_spu_size - (i + 1),
358                             total_spu_size
359                           );
360                       } /*if*/
361                   } /*if*/
362                 i = total_spu_size; /* indicate I'm finished */
363                 break;
364               } /*if*/
365             if (debug > 4)
366                 fprintf(stderr, "\tcmd(%5d): end cmd\n", i);
367           /* another SP_DCSQT follows */
368             thiscmdoffset = nextcmdoffset;
369             nextcmdoffset = read2(sub + thiscmdoffset + 2);
370             if (nextcmdoffset < dsize)
371               {
372                 if (debug > 0)
373                   {
374                     fprintf
375                       (
376                         stderr,
377                         "invalid control header nextcommand=%d dsize=%d!\n",
378                         nextcmdoffset,
379                         dsize
380                       );
381                   } /*if*/
382                 nextcmdoffset = thiscmdoffset;
383                 i = total_spu_size; /* force an end to all this */
384                 break;
385               } /*if*/
386             t = read2(sub + thiscmdoffset); /* SP_DCSQ_STM = delay in 90kHz units / 1024 before executing commands */
387             if (debug > 4)
388               {
389                 fprintf(stderr, "\tcmd(%5d): end cmd\n", i);
390                 fprintf(stderr, "\tBLK(%5d): time offset: %d; next: %d\n", i + 1, t, read2(sub + i + 3));
391               } /*if*/
392             if (debug > 4 && i + 1 < thiscmdoffset)
393               {
394                 fprintf(stderr, "next packet jump: %d bytes\n", thiscmdoffset - (i + 1));
395               } /*if*/
396             i = thiscmdoffset + 4; /* start of next lot of commands */
397         break;
398 
399       /* case SPU_CHG_COLCON: */ /* fixme: not handled */
400         default:
401             if (debug > 4)
402                 fprintf(stderr, "\tcmd(%5d): 0x%x\n", i, c);
403             if (debug > 0)
404                 fprintf(stderr, "invalid sequence in control header (%02x)!\n", c);
405             return -1;
406           } /*switch*/
407       } /*while*/
408 
409   /* now to decode the actual image data */
410     have_bits = false;
411     x = y = 0;
412     io = 0;
413     newspu->img = malloc(newspu->xd * newspu->yd);
414     if (ofs < 0 && y < newspu->yd)
415       {
416         fprintf(stderr, "WARN: No image data supplied for this subtitle\n");
417       }
418     else
419       {
420       /* decode image data */
421         while (ofs < dsize && y < newspu->yd)
422           {
423             i = get_next_bits();
424             if (i < 4)
425               {
426                 i = (i << 4) + get_next_bits();
427                 if (i < 16)
428                   {
429                     i = (i << 4) + get_next_bits();
430                     if (i < 0x40)
431                       {
432                         i = (i << 4) + get_next_bits();
433                         if (i < 4)
434                           {
435                             i = i + (newspu->xd - x) * 4; /* run ends at end of line */
436                           } /*if*/
437                       } /*if*/
438                   } /*if*/
439               } /*if*/
440             c = i & 3; /* pixel value */
441             i = i >> 2; /* count */
442             while (i--)
443               {
444                 newspu->img[io++] = c;
445                 if (++x == newspu->xd)
446                   {
447                   /* end of scanline */
448                     y += 2;
449                     x = 0;
450                     if (y >= newspu->yd && !(y & 1))
451                       {
452                       /* end of top (odd) field, now do bottom (even) field */
453                         y = 1;
454                         io = newspu->xd;
455                         ofs = ofs1;
456                       }
457                     else
458                         io += newspu->xd; /* next scanline */
459                     have_bits = false;
460                   } /*if*/
461               } /*while*/
462           } /*while*/
463       } /*if*/
464     if (newspu->pts[0] == -1)
465         return 0; /* fixme: free newspu or report error? */
466     newspu->pts[0] += add_offset;
467     if (newspu->pts[1] != -1)
468         newspu->pts[1] += add_offset;
469     addspu(newspu);
470     return 0;
471   } /*dvddecode*/
472 
473  /*
474   * from Y -> R
475   * from V -> G
476   * from U -> B
477   */
478 
ycrcb_to_rgb(int * Y,int * Cr,int * Cb)479 static void ycrcb_to_rgb(int *Y, int *Cr, int *Cb)
480 {
481     int R, G, B;
482     R = YCrCb2R(*Y,*Cr,*Cb);
483     G = YCrCb2G(*Y,*Cr,*Cb);
484     B = YCrCb2B(*Y,*Cr,*Cb);
485     *Y = R;
486     *Cr = G;
487     *Cb = B;
488 }
489 
absorb_palette(const struct dispdetails * d)490 static void absorb_palette(const struct dispdetails *d)
491   /* extracts the colour palette from d and puts it in RGB format into current_palette. */
492   {
493     int i;
494     for (i = 0; i < d->numpal; i++)
495       {
496         int Y, Cr, Cb;
497         Y = d->palette[i] >> 16 & 255;
498         Cr = d->palette[i] >> 8 & 255;
499         Cb = d->palette[i] & 255;
500         current_palette[i].r = YCrCb2R(Y, Cr, Cb);
501         current_palette[i].g = YCrCb2G(Y, Cr, Cb);
502         current_palette[i].b = YCrCb2B(Y, Cr, Cb);
503       } /*for*/
504   } /*absorb_palette*/
505 
pluck_pending_buttons()506 static void pluck_pending_buttons()
507   /* removes the head of pending_buttons, copies its palette into current_palette,
508     and gets rid of it. */
509   {
510     struct dispdetails * const d = pending_buttons;
511     int i;
512     pending_buttons = d->next;
513     absorb_palette(d);
514     for (i = 0; i < d->numbuttons; i++)
515       {
516         free(d->buttons[i].name);
517         free(d->buttons[i].up);
518         free(d->buttons[i].down);
519         free(d->buttons[i].left);
520         free(d->buttons[i].right);
521       } /*for*/
522     free(d->buttons);
523     free(d);
524   } /*pluck_pending_buttons*/
525 
cmap_find(int x,int y,const struct colormap * map,int nummap,int ci)526 static unsigned char cmap_find
527   (
528     int x,
529     int y,
530     const struct colormap *map, /* array */
531     int nummap, /* length of map array */
532     int ci /* pixel value */
533   )
534   /* returns the colour index (low nibble) and contrast (high nibble) of the
535     specified pixel, as determined from the highest-numbered entry of map that
536     covers its coordinates. Returns 0 if no entry is found. */
537   {
538     int i;
539     unsigned char cix = 0;
540     for (i = 0; i < nummap; i++)
541       /* fixme: why not just start from the other end and terminate when a match is found? */
542         if
543           (
544                 x >= map[i].x1
545             &&
546                 y >= map[i].y1
547             &&
548                 x <= map[i].x2
549             &&
550                 y <= map[i].y2
551           )
552             cix =
553                     (map[i].contrast >> ci * 4 & 15) << 4
554                 |
555                     map[i].color >> ci * 4 & 15;
556     return cix;
557   } /*cmap_find*/
558 
write_png(const char * file_name,const struct spu * s,const struct colormap * map,int nummap)559 static int write_png
560   (
561     const char *file_name,
562     const struct spu *s,
563     const struct colormap *map, /* array of entries for assigning colours to overlapping areas */
564     int nummap /* length of map array */
565   )
566   /* outputs the contents of s as a PNG file, converting pixels to colours
567     according to map. */
568   {
569     int status = -1; /* to begin with */
570     unsigned char *out_buf = NULL;
571     FILE *fp = NULL;
572     png_structp png_ptr = NULL;
573     png_infop info_ptr = NULL;
574     const unsigned short subwidth = svcd_adjust ? 704 : 720;
575     const unsigned short subheight = video_format == VF_NTSC ? 480 : 576;
576     do /*once*/
577       {
578         out_buf = malloc(s->xd * s->yd * 4);
579         if (out_buf == NULL)
580           {
581             fprintf(stderr, "ERR:  unable allocate %d-byte PNG buffer\n", s->xd * s->yd * 4);
582             break;
583           } /*if*/
584           {
585             unsigned char *temp = out_buf;
586             bool nonzero = false;
587             unsigned int x, y;
588             for (y = 0; y < s->yd; y++)
589               {
590                 for (x = 0; x < s->xd; x++)
591                   {
592                     const unsigned char cix =
593                         cmap_find(x + s->x0, y + s->y0, map, nummap, s->img[y * s->xd + x]);
594                     *temp++ = current_palette[cix & 15].r;
595                     *temp++ = current_palette[cix & 15].g;
596                     *temp++ = current_palette[cix & 15].b;
597                     *temp++ = (cix >> 4) * 17;
598                     if (cix & 0xf0)
599                         nonzero = true;
600                   } /*for*/
601               } /*for*/
602             if (!nonzero)
603               {
604               /* all transparent, don't bother writing any image */
605                 status = 1;
606                 break;
607               } /*if*/
608           }
609         fp = fopen(file_name, "wb");
610         if (fp == NULL)
611           {
612             fprintf(stderr, "ERR:  error %s trying to open/create file: %s\n", strerror(errno), file_name);
613             break;
614           } /*if*/
615         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
616         if (png_ptr == NULL)
617             break;
618         info_ptr = png_create_info_struct(png_ptr);
619         if (info_ptr == NULL)
620             break;
621         if (setjmp(png_jmpbuf(png_ptr)))
622             break;
623         png_init_io(png_ptr, fp);
624         png_set_filter(png_ptr, 0, PNG_FILTER_NONE);
625         png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
626         png_set_compression_mem_level(png_ptr, 8);
627         png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
628         png_set_compression_window_bits(png_ptr, 15);
629         png_set_compression_method(png_ptr, 8);
630         if (full_size)
631           {
632             png_set_IHDR
633               (
634                 /*png_ptr =*/ png_ptr,
635                 /*info_ptr =*/ info_ptr,
636                 /*width =*/ subwidth,
637                 /*height =*/ subheight,
638                 /*bit_depth =*/ 8,
639                 /*color_type =*/ PNG_COLOR_TYPE_RGB_ALPHA,
640                 /*interlace_method =*/ PNG_INTERLACE_NONE,
641                 /*compression_method =*/ PNG_COMPRESSION_TYPE_DEFAULT,
642                 /*filter_method =*/ PNG_FILTER_TYPE_DEFAULT
643               );
644           }
645         else
646           {
647             png_set_IHDR
648               (
649                 /*png_ptr =*/ png_ptr,
650                 /*info_ptr =*/ info_ptr,
651                 /*width =*/ s->xd,
652                 /*height =*/ s->yd,
653                 /*bit_depth =*/ 8,
654                 /*color_type =*/ PNG_COLOR_TYPE_RGB_ALPHA,
655                 /*interlace_method =*/ PNG_INTERLACE_NONE,
656                 /*compression_method =*/ PNG_COMPRESSION_TYPE_DEFAULT,
657                 /*filter_method =*/ PNG_FILTER_TYPE_DEFAULT
658               );
659           } /*if*/
660         png_write_info(png_ptr, info_ptr);
661         png_set_packing(png_ptr);
662           {
663             unsigned int xd = s->xd, yd = s->yd;
664             png_byte *row_pointers[576]; /* big enough for both PAL and NTSC */
665             unsigned int a, x, y;
666             if (full_size)
667               {
668                 unsigned char *image;
669                 const unsigned char *temp = out_buf;
670                 image = malloc(subwidth * subheight * 4);
671                 memset(image, 0, subwidth * subheight * 4);    // fill image full transparent
672                 // insert image on the correct position
673                 for (y = s->y0; y < s->y0 + s->yd; y++)
674                   {
675                     unsigned char *to = &image[y * subwidth * 4 + s->x0 * 4];
676                     if (y >= subheight)
677                       {
678                         fprintf(stderr, "WARN: subtitle %s truncated\n", file_name);
679                         break;
680                       } /*if*/
681                     for (x = 0; x < s->xd; x++)
682                       {
683                         *to++ = *temp++;
684                         *to++ = *temp++;
685                         *to++ = *temp++;
686                         *to++ = *temp++;
687                       } /*for*/
688                   } /*for*/
689                 yd = subheight;
690                 xd = subwidth;
691                 free(out_buf);
692                 out_buf = image;
693               } /*if*/
694             for (a = 0; a < yd; a++)
695               {
696                 row_pointers[a] = out_buf + a * (xd * 4);
697               } /*for*/
698             png_write_image(png_ptr, row_pointers);
699           }
700         png_write_end(png_ptr, info_ptr);
701       /* all successfully done */
702         status = 0;
703       }
704     while (false);
705     if (png_ptr != NULL)
706       {
707         png_destroy_write_struct(&png_ptr, &info_ptr);
708       } /*if*/
709     if (fp != NULL)
710       {
711         fclose(fp);
712       } /*if*/
713     free(out_buf);
714     return
715         status;
716   } /*write_png*/
717 
write_pts(const char * preamble,int pts)718 static void write_pts(const char *preamble, int pts)
719   /* outputs a formatted representation of a timestamp to fdo. */
720   {
721     fprintf
722       (
723         fdo,
724         " %s=\"%02d:%02d:%02d.%02d\"",
725         preamble,
726         (pts / (60 * 60 * 90000)) % 24,
727         (pts / (60 * 90000)) % 60,
728         (pts / 90000) % 60,
729         (pts / 900) % 100
730       );
731   } /*write_pts*/
732 
733 /*
734   copy the content of buf to expbuf converting '&' '<' '>' '"'
735   expbuf must be big enough to contain the expanded buffer
736 */
xml_buf(unsigned char * expbuf,const unsigned char * buf)737 static void xml_buf
738   (
739     unsigned char *expbuf,
740     const unsigned char *buf
741   )
742   {
743     const unsigned char *p;
744     do
745       {
746         switch (*buf)
747           {
748         case '&':
749             p = (const unsigned char *)"&amp;";
750         break;
751         case '<':
752             p = (const unsigned char *)"&lt;";
753         break;
754         case '>':
755             p = (const unsigned char *)"&gt;";
756         break;
757         case '"':
758             p = (const unsigned char *)"&quot;";
759         break;
760         default:
761             p = NULL;
762         break;
763           } /*switch*/
764         if (p)
765           {
766             while ((*expbuf++ = *p++))
767                /* copy the representation */;
768             --expbuf;
769           }
770         else
771             *expbuf++ = *buf; /* copy as is */
772       }
773     while (*buf++);
774   } /*xml_buf*/
775 
write_menu_image(const struct spu * s,const struct dispdetails * d,const char * type,int offset)776 static void write_menu_image
777   (
778     const struct spu *s,
779     const struct dispdetails *d,
780     const char *type, /* name of attribute to fill in with name of generated file */
781     int offset /* 0 => highlighted, 1 => selected */
782   )
783   /* outputs the subpicture image with the buttons in the highlighted or selected state. */
784   {
785     unsigned char nbuf[256];
786     int nummap = d->numbuttons + 1, i;
787     struct colormap *map = malloc(sizeof(struct colormap) * nummap);
788     memset(map, 0, sizeof(struct colormap)); // set the first one blank
789     map[0].x2 = 0x7fffffff;
790     map[0].y2 = 0x7fffffff;
791     for (i = 0; i < d->numbuttons; i++)
792       {
793         const uint32_t cc = d->coli[2 * d->buttons[i].grp - 2 + offset];
794         map[i + 1].x1 = d->buttons[i].x1;
795         map[i + 1].y1 = d->buttons[i].y1;
796         map[i + 1].x2 = d->buttons[i].x2;
797         map[i + 1].y2 = d->buttons[i].y2;
798         map[i + 1].color = cc >> 16;
799         map[i + 1].contrast = cc;
800       } /*for*/
801     sprintf((char *)nbuf, "%s%05d%c.png", base_name, s->subno, type[0]);
802     if (!write_png((char *)nbuf, s, map, nummap))
803       {
804         unsigned char ebuf[sizeof nbuf * 6];
805         xml_buf(ebuf, nbuf);
806         fprintf(fdo," %s=\"%s\"", type, ebuf);
807       } /*if*/
808     free(map);
809   } /*write_menu_image*/
810 
write_spu(const struct spu * curspu,const struct dispdetails * buttons)811 static void write_spu
812   (
813     const struct spu * curspu,
814     const struct dispdetails * buttons /* applicable button highlight info, if any */
815   )
816   /* writes out all information about a subpicture unit as an <spu> tag. */
817   {
818     unsigned char nbuf[256];
819     int i;
820     if (buttons)
821         absorb_palette(buttons);
822     fprintf(fdo, "\t\t<spu");
823     sprintf((char *)nbuf, "%s%05d.png", base_name, curspu->subno);
824     if (!write_png((char *)nbuf, curspu, curspu->map, curspu->nummap))
825       {
826         unsigned char ebuf[sizeof nbuf * 6];
827         xml_buf(ebuf, nbuf);
828         fprintf(fdo, " image=\"%s\"", ebuf);
829       } /*if*/
830     if (buttons && buttons->numbuttons)
831       {
832         write_menu_image(curspu, buttons, "highlight", 0);
833         write_menu_image(curspu, buttons, "select", 1);
834       } /*if*/
835     write_pts("start", curspu->pts[0]);
836     if (curspu->pts[1] != -1)
837         write_pts("end", curspu->pts[1]);
838     if (curspu->x0 || curspu->y0)
839         fprintf(fdo, " xoffset=\"%d\" yoffset=\"%d\"", curspu->x0, curspu->y0);
840     if (curspu->force_display)
841         fprintf(fdo, " force=\"yes\"");
842     if (buttons && buttons->numbuttons)
843       {
844         fprintf(fdo, ">\n");
845         for (i = 0; i < buttons->numbuttons; i++)
846           {
847             const struct button * const b = buttons->buttons + i;
848             fprintf
849               (
850                 fdo,
851                 "\t\t\t<%s name=\"%s\" x0=\"%d\" y0=\"%d\" x1=\"%d\" y1=\"%d\""
852                     " up=\"%s\" down=\"%s\" left=\"%s\" right=\"%s\" />\n",
853                 b->autoaction ? "action" : "button", b->name,
854                 b->x1, b->y1, b->x2, b->y2,
855                 b->up, b->down, b->left, b->right
856               );
857           } /*for*/
858         fprintf(fdo, "\t\t</spu>\n");
859       }
860     else
861         fprintf(fdo, " />\n");
862   } /*write_spu*/
863 
flushspus(unsigned int lasttime)864 static void flushspus(unsigned int lasttime)
865   /* pops and outputs elements from pending_spus and pending_buttons that start
866     prior to lasttime. */
867   {
868     while (pending_spus)
869       {
870         const struct spu * const curspu = pending_spus;
871         if (curspu->pts[0] >= lasttime)
872             return; /* next entry not yet due */
873         pending_spus = pending_spus->next;
874         while
875           (
876                 pending_buttons
877             &&
878                 pending_buttons->pts[1] < curspu->pts[0]
879             &&
880                 pending_buttons->pts[1] != -1
881           )
882           /* merge colours from expired entries into colour table, but otherwise ignore them */
883             pluck_pending_buttons();
884         if
885           (
886                 pending_buttons
887             &&
888                 (pending_buttons->pts[0] < curspu->pts[1] || curspu->pts[1] == -1)
889             &&
890                 (pending_buttons->pts[1] > curspu->pts[0] || pending_buttons->pts[1] == -1)
891           )
892           /* head of pending_buttons overlaps duration of curspu */
893             write_spu(curspu, pending_buttons);
894         else
895             write_spu(curspu, 0);
896         free(curspu->img);
897         free(curspu->map);
898         free((void *)curspu);
899       } /*while*/
900   } /*flushspus*/
901 
902 #define bps(n,R,G,B) do { current_palette[n].r = R; current_palette[n].g = G; current_palette[n].b = B; } while (false)
903 
svcddecode()904 static int svcddecode()
905   {
906     unsigned int io;
907     unsigned short int size, i, x, y;
908     unsigned char c;
909     struct spu *s;
910     int n;
911     size = read2(sub);
912     if (debug > 1)
913         fprintf(stderr, "packet: 0x%x bytes\n", size);
914     s = malloc(sizeof(struct spu));
915     memset(s, 0, sizeof(struct spu));
916     s->subno = subno++;
917     s->pts[0] = spts;
918     s->pts[1] = -1;
919     s->nummap = 1;
920     s->map = malloc(sizeof(struct colormap));
921     memset(s->map, 0, sizeof(struct colormap));
922     s->map[0].x2 = 0x7ffffff; /* single colour map covers entire picture */
923     s->map[0].y2 = 0x7ffffff;
924     i = 2;
925     if (sub[i] & 0x08) /* timestamp present */
926       {
927         s->pts[1] = spts + read4(sub + i + 2);
928         i += 4;
929       } /*if*/
930     i += 2;
931     s->x0 = read2(sub + i);
932     s->y0 = read2(sub + i + 2);
933     s->xd = read2(sub + i + 4);
934     s->yd = read2(sub + i + 6);
935     i += 8;
936     if (debug > 4)
937         fprintf(stderr, "img ofs: %d,%d  size: %d,%d\n", s->x0, s->y0, s->xd, s->yd);
938     for (n = 0; n < 4; n++)
939       {
940       /* collect colour table */
941         int r, g, b;
942         r = sub[i + 0 + n * 4];
943         g = sub[i + 1 + n * 4];
944         b = sub[i + 2 + n * 4];
945         ycrcb_to_rgb(&r, &g, &b);
946         bps(n, r, g, b);
947         if (debug > 4)
948           {
949             fprintf
950               (
951                 stderr,
952                 "palette: %d => 0x%02x 0x%02x 0x%02x 0x%02x => (%d, %d, %d)\n",
953                 n,
954                 sub[i + 0 + n * 4],
955                 sub[i + 1 + n * 4],
956                 sub[i + 2 + n * 4],
957                 sub[i + 3 + n * 4],
958                 r,
959                 g,
960                 b
961               );
962           } /*if*/
963       } /*for*/
964     s->map[0].color = 0x3210;
965     s->map[0].contrast =
966             (sub[i + 3] >> 4)
967         +
968             (sub[i + 7] & 0xf0)
969         +
970             ((sub[i + 11] & 0xf0) << 4)
971         +
972             ((sub[i + 15] & 0xf0) << 8);
973     if (debug > 4)
974         fprintf(stderr, "tpalette: %04x\n", s->map[0].contrast);
975     i += 16;
976     if (sub[i++] >> 6)
977       {
978         if (debug > 4)
979           {
980             fprintf
981               (
982                 stderr,
983                 "cmd: shift (unsupported), direction=%d time=%f\n",
984                 sub[i - 1] >> 4 & 0x3,
985                 read4(sub) / 90000.0
986               );
987           } /*if*/
988         i += 4;
989       } /*if*/
990     ofs = i + 2 - 1; // get_next_svcdbits will increment ofs by 1
991     ofs1 = ofs + read2(sub + i);
992   /* i += 2; */ /* not further used */
993     if (debug > 4)
994         fprintf(stderr, "cmd: image offsets 0x%x 0x%x\n", ofs, ofs1);
995     have_bits = 0;
996     x = y = 0;
997     io = 0;
998     s->img = malloc(s->xd * s->yd);
999     memset(s->img, 0, s->xd * s->yd);
1000   /* decode the pixels */
1001     while (ofs < size && y < s->yd)
1002       {
1003         if ((c = get_next_svcdbits()) != 0)
1004           {
1005             s->img[io++] = c;
1006             ++x;
1007           }
1008         else
1009           {
1010             c = get_next_svcdbits() + 1;
1011             x += c;
1012             io += c;
1013           } /*if*/
1014         if (x >= s->xd)
1015           {
1016             y += 2;
1017             x = 0;
1018             if (y >= s->yd && !(y & 1))
1019               {
1020                 y = 1;
1021                 ofs = ofs1;
1022               } /*if*/
1023             io = s->xd * y;
1024             have_bits = 0;
1025           } /*if*/
1026       } /*while*/
1027     s->pts[0] += add_offset;
1028     if (s->pts[1] != -1)
1029         s->pts[1] += add_offset;
1030     addspu(s);
1031     if (debug > 2)
1032         fprintf(stderr, "ofs: 0x%x y: %d\n", ofs, y);
1033     return 0;
1034    } /*svcddecode*/
1035 
usage(void)1036 static void usage(void)
1037   {
1038     fprintf(stderr,
1039         "\nUse: %s [options] [input file] [input file] ...\n\n",
1040         "spuunmux");
1041     fprintf(stderr, "options:\n");
1042     fprintf(stderr,
1043         "-o <name>   base name for script and images     [sub]\n");
1044     fprintf(stderr,
1045         "-v <level>  verbosity level                     [0]\n");
1046     fprintf(stderr,
1047         "-f          resize images to full size          [720x576 or 720x480]\n");
1048     fprintf(stderr,
1049         "-F <format> specify video format, NTSC or PAL\n");
1050     fprintf(stderr,
1051         "-s <stream> number of the substream to extract  [0]\n");
1052     fprintf(stderr,
1053         "-p <file>   name of file with dvd palette       [none]\n");
1054     fprintf(stderr, "            if palette file ends with .rgb\n");
1055     fprintf(stderr, "                treated as a RGB\n");
1056     fprintf(stderr, "                else as a YCbCr color\n");
1057     fprintf(stderr, "-h          print this help\n");
1058     fprintf(stderr, "-V          print version number\n");
1059     fprintf(stderr, "\n");
1060   } /*usage*/
1061 
main(int argc,char ** argv)1062 int main(int argc, char **argv)
1063   {
1064     int option, n;
1065     int firstvideo = -1;
1066     unsigned int pid, next_word, stream_number, fileindex, nrinfiles;
1067     unsigned char cbuf[CBUFSIZE];
1068     unsigned char psbuf[PSBUFSIZE];
1069     char *palet_file;
1070     char *iname[256]; /* names of input files -- fixme: no range checking */
1071     unsigned int last_system_time = -1;
1072 
1073     video_format = get_video_format();
1074     fputs(PACKAGE_HEADER("spuunmux"), stderr);
1075     if (video_format != VF_NONE)
1076       {
1077         fprintf
1078           (
1079             stderr,
1080             "INFO: default video format is %s\n",
1081             video_format == VF_PAL ? "PAL" : "NTSC"
1082           );
1083       }
1084     else
1085       {
1086 #if defined(DEFAULT_VIDEO_FORMAT)
1087 #    if DEFAULT_VIDEO_FORMAT == 1
1088         fprintf(stderr, "INFO: default video format is NTSC\n");
1089         video_format = VF_NTSC;
1090 #    elif DEFAULT_VIDEO_FORMAT == 2
1091         fprintf(stderr, "INFO: default video format is PAL\n");
1092         video_format = VF_PAL;
1093 #    endif
1094 #else
1095         fprintf(stderr, "INFO: no default video format, must explicitly specify NTSC or PAL\n");
1096 #endif
1097       } /*if*/
1098     base_name = "sub";
1099     stream_number = 0;
1100     palet_file = 0;
1101     while ((option = getopt(argc, argv, "o:v:fF:s:p:Vh")) != -1)
1102       {
1103         switch (option)
1104           {
1105         case 'o':
1106             base_name = optarg;
1107         break;
1108         case 'v':
1109             debug = strtounsigned(optarg, "verbosity");
1110         break;
1111         case 'f':
1112             full_size = true;
1113         break;
1114         case 'F':
1115             if (!strcasecmp(optarg, "ntsc"))
1116               {
1117                 video_format = VF_NTSC;
1118               }
1119             else if (!strcasecmp(optarg, "pal"))
1120               {
1121                 video_format = VF_PAL;
1122               }
1123             else
1124               {
1125                 fprintf(stderr, "ERR:  Unrecognized video format \"%s\"\n", optarg);
1126                 exit(-1);
1127               } /*if*/
1128         break;
1129         case 's':
1130             stream_number = strtounsigned(optarg, "stream number");
1131         break;
1132         case 'p':
1133             palet_file = optarg;
1134         break;
1135         case 'V':
1136             exit(-1);
1137 
1138         case 'h':
1139         default:
1140             usage();
1141             return -1;
1142           } /*switch*/
1143       } /*while*/
1144 
1145     if (optind < argc)
1146       {
1147       /* remaining args are input filenames */
1148         int n, i;
1149         for (i = 0, n = optind; n < argc; n++, i++)
1150             iname[i] = argv[n];
1151         nrinfiles = i;
1152       }
1153     else
1154       {
1155         usage();
1156         return -1;
1157       } /*if*/
1158     if (full_size && video_format == VF_NONE)
1159       {
1160         fprintf(stderr, "ERR:  cannot determine meaning of full size without knowing if it's NTSC or PAL\n");
1161         exit(-1);
1162       } /*if*/
1163 
1164   /* initialize current_palette to default palette */
1165     bps(0, 0, 0, 0);
1166     bps(1, 127, 0, 0);
1167     bps(2, 0, 127, 0);
1168     bps(3, 127, 127, 0);
1169     bps(4, 0, 0, 127);
1170     bps(5, 127, 0, 127);
1171     bps(6, 0, 127, 127);
1172     bps(7, 127, 127, 127);
1173     bps(8, 192, 192, 192);
1174     bps(9, 128, 0, 0);
1175     bps(10, 0, 128, 0);
1176     bps(11, 128, 128, 0);
1177     bps(12, 0, 0, 128);
1178     bps(13, 128, 0, 128);
1179     bps(14, 0, 128, 128);
1180     bps(15, 128, 128, 128);
1181 
1182     if (palet_file)
1183       {
1184         bool rgb = false;
1185         char * const temp = strrchr(palet_file, '.');
1186         if (temp != NULL)
1187           {
1188             if (strcmp(temp, ".rgb") == 0)
1189                 rgb = true;
1190           } /*if*/
1191         fdo = fopen(palet_file, "r");
1192         if (fdo != NULL)
1193           {
1194             for (n = 0; n < 16; n++)
1195               {
1196                 int r, g, b;
1197                 fscanf(fdo, "%02x%02x%02x", &r, &g, &b);
1198                 if (!rgb)
1199                     ycrcb_to_rgb(&r, &g, &b);
1200                 current_palette[n].r = r;
1201                 current_palette[n].g = g;
1202                 current_palette[n].b = b;
1203                 if (debug > 3)
1204                     fprintf
1205                       (
1206                         stderr,
1207                         "pal: %d #%02x%02x%02x\n",
1208                         n, current_palette[n].r, current_palette[n].g, current_palette[n].b
1209                       );
1210               } /*for*/
1211             fclose(fdo);
1212           }
1213         else
1214           {
1215             fprintf(stderr, "unable to open %s, using defaults\n", palet_file);
1216           } /*if*/
1217       } /*if*/
1218     if (strlen(base_name) > 246)
1219       {
1220         fprintf(stderr,
1221             "error: max length of base for filename creation is 246 characters\n");
1222         return -1;
1223       } /*if*/
1224       {
1225         char nbuf[256];
1226         sprintf(nbuf, "%s.xml", base_name);
1227         fdo = fopen(nbuf, "w+");
1228       }
1229     fprintf(fdo, "<subpictures>\n\t<stream>\n");
1230     pts = 0;
1231     subno = 0;
1232     subi = 0;
1233     add_offset = 450; // for rounding purposes
1234     fileindex = 0;
1235     while (fileindex < nrinfiles)
1236       {
1237         struct vfile fd = varied_open(iname[fileindex], O_RDONLY, "input file");
1238         if (debug > 0)
1239             fprintf(stderr, "file: %s\n", iname[fileindex]);
1240         fileindex++;
1241         while (fread(&pid, 1, 4, fd.h) == 4)
1242           {
1243             pid = ntohl(pid);
1244             if (pid == 0x00000100 + MPID_PACK)
1245               {  // start PS (Program stream)
1246                 unsigned int new_system_time, stuffcount;
1247 l_01ba:
1248                 if (debug > 5)
1249                     fprintf(stderr, "pack_start_code\n");
1250                 if (fread(psbuf, 1, PSBUFSIZE, fd.h) < 1)
1251                     break;
1252                 if ((psbuf[0] & 0xc0) != 0x40)
1253                   {
1254                     if (debug > 1)
1255                         fprintf(stderr, "not a MPEG-2 file, skipping.\n");
1256                     break;
1257                   } /*if*/
1258                 new_system_time =
1259                         (psbuf[4] >> 3)
1260                     +
1261                         psbuf[3] * 32
1262                     +
1263                         (psbuf[2] & 3) * 32 * 256
1264                     +
1265                         (psbuf[2] & 0xf8) * 32 * 128
1266                     +
1267                         psbuf[1] * 1024 * 1024
1268                     +
1269                         (psbuf[0] & 3) * 1024 * 1024 * 256
1270                     +
1271                         (psbuf[0] & 0x38) * 1024 * 1024 * 128;
1272                 if (new_system_time < last_system_time)
1273                   {
1274                     if (last_system_time != -1)
1275                       {
1276                         if (debug > 0)
1277                             fprintf
1278                               (
1279                                 stderr,
1280                                 "Time changed in stream header, use old time as offset for"
1281                                     " timecode in subtitle stream\n"
1282                               );
1283                         add_offset += last_system_time;
1284                       } /*if*/
1285                   } /*if*/
1286                 last_system_time = new_system_time;
1287                 flushspus(last_system_time);
1288                 if (debug > 5)
1289                   {
1290                     fprintf(stderr, "system time: %u\n", new_system_time);
1291                   } /*if*/
1292                 stuffcount = psbuf[9] & 7;
1293                 if (stuffcount != 0)
1294                   {
1295                     char stuff[7];
1296                     if (debug > 5)
1297                         fprintf(stderr, "found %d stuffing bytes\n", stuffcount);
1298                     if (fread(stuff, 1, stuffcount, fd.h) < stuffcount)
1299                         break;
1300                   } /*if*/
1301               }
1302             else if (pid == 0x100 + MPID_PROGRAM_END)
1303               {
1304                 if (debug > 5)
1305                     fprintf(stderr, "end packet\n");
1306               }
1307             else /* packet with a length field */
1308               {
1309                 unsigned short int package_length;
1310                 fread(&package_length, 1, 2, fd.h);
1311                 package_length = ntohs(package_length);
1312                 if (package_length != 0)
1313                   {
1314                     switch (pid)
1315                       {
1316                     case 0x0100 + MPID_SYSTEM:
1317                         if (debug > 5)
1318                             fprintf(stderr, "system header\n");
1319                     break;
1320                     case 0x0100 + MPID_PRIVATE2: /* PCI & DSI packets, not my problem */
1321                         if (debug > 5)
1322                             fprintf(stderr, "private stream 2\n");
1323                     break;
1324                     case 0x0100 + MPID_PRIVATE1: /* subpicture or audio stream */
1325                         if (debug > 5)
1326                             fprintf(stderr, "private stream 1\n");
1327                         fread(cbuf, 1, package_length, fd.h);
1328                         next_word = getpts(cbuf);
1329                         if (next_word != -1)
1330                           {
1331                             pts = next_word;
1332                           } /*if*/
1333                         next_word = cbuf[2] /* additional data length */ + 3 /* length of fixed part of MPEG-2 extension */;
1334                         if (debug > 5)
1335                           {
1336                           /* dump PES header + extension */
1337                             int c;
1338                             for (c = 0; c < next_word; c++)
1339                                 fprintf(stderr, "0x%02x ", cbuf[c]);
1340                           } /*if*/
1341                         if (debug > 5)
1342                             fprintf(stderr, "tid: %d\n", pts);
1343                         if
1344                           (
1345                                 cbuf[next_word] == stream_number + 32 /* DVD-Video stream nr */
1346                             ||
1347                                 cbuf[next_word] == 0x70 && cbuf[next_word + 1] == stream_number
1348                                   /* SVCD stream nr */
1349                           )
1350                           {
1351                           /* this is the subpicture stream the user wants dumped */
1352                             svcd_adjust = cbuf[next_word] == 0x70 ? 4 : 0;
1353                             if (/*debug < 6 &&*/ debug > 1)
1354                               {
1355                                 fprintf(stderr,
1356                                     "id: 0x%x 0x%x %d  tid: %d\n",
1357                                     cbuf[next_word], package_length,
1358                                     next_word, pts);
1359                               } /*if*/
1360                             if (!subi)
1361                               {
1362                               /* starting a new SPU */
1363                                 subs =
1364                                         ((unsigned int)cbuf[next_word + 1 + svcd_adjust] << 8)
1365                                     +
1366                                         cbuf[next_word + 2 + svcd_adjust];
1367                                   /* SPDSZ, size of total subpicture data */
1368                                 spts = pts;
1369                               } /*if*/
1370                             memcpy
1371                               (
1372                                 /*dest =*/ sub + subi,
1373                                 /*src =*/ cbuf + next_word + 1 + svcd_adjust,
1374                                 /*n =*/ package_length - next_word - 1 - svcd_adjust
1375                               );
1376                               /* collect the subpicture data */
1377                             if (debug > 1)
1378                               {
1379                                 fprintf(stderr, "found %d bytes of data\n",
1380                                     package_length - next_word - 1 - svcd_adjust);
1381                               } /*if*/
1382                             subi += package_length - next_word - 1 - svcd_adjust;
1383                               /* how much I just collected */
1384                             if (debug > 2)
1385                               {
1386                                 fprintf(stderr,
1387                                     "subi: %d (0x%x)  subs: %d (0x%x) b-a-1: %d (0x%x)\n",
1388                                     subi, subi, subs, subs,
1389                                     package_length - next_word - 1 - svcd_adjust,
1390                                     package_length - next_word - 1 - svcd_adjust);
1391                               } /*if*/
1392                             if (svcd_adjust)
1393                               {
1394                                 if (cbuf[next_word + 2] & 0x80)
1395                                   {
1396                                     subi = 0;
1397                                     next_word = svcddecode();
1398                                     if (next_word)
1399                                       {
1400                                         fprintf
1401                                           (
1402                                             stderr,
1403                                             "found unreadable subtitle at %.2fs, skipping\n",
1404                                             (double) spts / 90000
1405                                           );
1406                                         continue;
1407                                       } /*if*/
1408                                   } /*if*/
1409                               }
1410                             else if (subs == subi)
1411                               {
1412                               /* got a complete SPU */
1413                                 subi = 0;
1414                                 if (dvddecode())
1415                                   {
1416                                     fprintf(stderr,
1417                                         "found unreadable subtitle at %.2fs, skipping\n",
1418                                         (double) spts / 90000);
1419                                     continue;
1420                                   } /*if*/
1421                               } /*if*/
1422                           } /*if dump the stream*/
1423                         package_length = 0;
1424                     break;
1425                     case 0x0100 + MPID_VIDEO_FIRST:
1426                         if (firstvideo == -1)
1427                           {
1428                             fread(cbuf, 1, package_length, fd.h);
1429                             firstvideo = getpts(cbuf);
1430                             add_offset -= firstvideo;
1431                             package_length = 0;
1432                           } /*if*/
1433                         if (debug > 5)
1434                             fprintf(stderr, "video stream 0\n");
1435                     break;
1436                     case 0x01e1:
1437                     case 0x01e2:
1438                     case 0x01e3:
1439                     case 0x01e4:
1440                     case 0x01e5:
1441                     case 0x01e6:
1442                     case 0x01e7:
1443                     case 0x01e8:
1444                     case 0x01e9:
1445                     case 0x01ea:
1446                     case 0x01eb:
1447                     case 0x01ec:
1448                     case 0x01ed:
1449                     case 0x01ee:
1450                     case 0x01ef:
1451                         if (debug > 5)
1452                             fprintf(stderr, "video stream %d\n", pid - 0x100 - MPID_VIDEO_FIRST);
1453                     break;
1454                     case 0x0100 + MPID_PAD:
1455                         if (debug > 5)
1456                             fprintf(stderr, "padding stream %d bytes\n", package_length);
1457                         fread(cbuf, 1, package_length, fd.h);
1458                         if (package_length > 30)
1459                           {
1460                             int i;
1461                             package_length = 0;
1462                             i = 0;
1463                             if (strcmp((const char *)cbuf + i, "dvdauthor-data"))
1464                                 break;
1465                           /* pad packet contains DVDAuthor private data */
1466                             i = 15;
1467                             if (cbuf[i] != 2)
1468                                 break;
1469                             switch(cbuf[i + 1])
1470                               {
1471                             case 1: // subtitle/menu color and button information
1472                               {
1473                                 // int st = cbuf[i + 2] & 31; // we ignore which subtitle stream for now
1474                                 struct dispdetails *buttons;
1475                                 i += 3;
1476                                 buttons = malloc(sizeof(struct dispdetails));
1477                                 memset(buttons, 0, sizeof(struct dispdetails));
1478                                 buttons->pts[0] = read4(cbuf + i);
1479                                 buttons->pts[1] = read4(cbuf + i + 4);
1480                                 i += 8;
1481                                 while(cbuf[i] != 0xff)
1482                                   {
1483                                     switch(cbuf[i])
1484                                       {
1485                                     case 1: /* colour table */
1486                                       {
1487                                         int j;
1488                                         buttons->numpal = 0;
1489                                         for (j = 0; j < cbuf[i + 1]; j++)
1490                                           {
1491                                             const int c = read4(cbuf + i + 1 + 3 * j) & 0xffffff;
1492                                             buttons->palette[j] = c;
1493                                             buttons->numpal++;
1494                                           } /*for*/
1495                                         i += 2 + 3 * buttons->numpal;
1496                                       }
1497                                     break;
1498                                     case 2: /* button groups */
1499                                       {
1500                                         int j;
1501                                         buttons->numcoli = cbuf[i + 1];
1502                                         for (j = 0; j < 2 * buttons->numcoli; j++)
1503                                             buttons->coli[j] = read4(cbuf + i + 2 + j * 4);
1504                                         i += 2 + 8 * buttons->numcoli;
1505                                       }
1506                                     break;
1507                                     case 3: /* button placement */
1508                                       {
1509                                         int j;
1510                                         buttons->numbuttons = cbuf[i + 1];
1511                                         buttons->buttons = malloc(buttons->numbuttons * sizeof(struct button));
1512                                         i += 2;
1513                                         for (j = 0; j < buttons->numbuttons; j++)
1514                                           {
1515                                             struct button *b = &buttons->buttons[j];
1516                                             b->name = readpstr(cbuf, &i);
1517                                             i += 2;
1518                                             b->autoaction = cbuf[i++] != 0;
1519                                             b->grp = cbuf[i];
1520                                             b->x1 = read2(cbuf + i + 1);
1521                                             b->y1 = read2(cbuf + i + 3);
1522                                             b->x2 = read2(cbuf + i + 5);
1523                                             b->y2 = read2(cbuf + i + 7);
1524                                             i += 9;
1525                                             // up down left right
1526                                             b->up = readpstr(cbuf, &i);
1527                                             b->down = readpstr(cbuf, &i);
1528                                             b->left = readpstr(cbuf, &i);
1529                                             b->right = readpstr(cbuf, &i);
1530                                           } /*for*/
1531                                       }
1532                                     break;
1533                                     default:
1534                                         fprintf(stderr,"ERR:  unknown dvd info packet command: %d, offset %d\n",cbuf[i], i);
1535                                         exit(1);
1536                                       } /*switch*/
1537                                   } /*while*/
1538                                 add_pending_buttons(buttons);
1539                               } /*case 1*/
1540                             break;
1541                               } /*switch*/
1542                           } /*if*/
1543                         package_length = 0;
1544                     break;
1545                     case 0x01c0:
1546                     case 0x01c1:
1547                     case 0x01c2:
1548                     case 0x01c3:
1549                     case 0x01c4:
1550                     case 0x01c5:
1551                     case 0x01c6:
1552                     case 0x01c7:
1553                     case 0x01c8:
1554                     case 0x01c9:
1555                     case 0x01ca:
1556                     case 0x01cb:
1557                     case 0x01cc:
1558                     case 0x01cd:
1559                     case 0x01ce:
1560                     case 0x01cf:
1561                     case 0x01d0:
1562                     case 0x01d1:
1563                     case 0x01d2:
1564                     case 0x01d3:
1565                     case 0x01d4:
1566                     case 0x01d5:
1567                     case 0x01d6:
1568                     case 0x01d7:
1569                     case 0x01d8:
1570                     case 0x01d9:
1571                     case 0x01da:
1572                     case 0x01db:
1573                     case 0x01dc:
1574                     case 0x01dd:
1575                     case 0x01de:
1576                     case 0x01df:
1577                         if (debug > 5)
1578                             fprintf(stderr, "audio stream %d\n", pid - 0x100 - MPID_AUDIO_FIRST);
1579                     break;
1580                     default:
1581                         if (debug > 0)
1582                             fprintf(stderr, "unknown header %x\n", pid);
1583                         next_word = pid << 16 | package_length;
1584                         package_length = 2;
1585                         while (next_word != 0x100 + MPID_PACK)
1586                           {
1587                             next_word = next_word << 8;
1588                             if (fread(&next_word, 1, 1, fd.h) < 1)
1589                                 break;
1590                             package_length++;
1591                           } /*while*/
1592                         if (debug > 0)
1593                             fprintf(stderr, "skipped %d bytes of garbage\n", package_length);
1594                         goto l_01ba;
1595                       } /*switch*/
1596                     fread(cbuf, 1, package_length, fd.h);
1597                   } /*if*/
1598               } /*if*/
1599           } /*while read next packet header*/
1600         varied_close(fd);
1601       } /*while fileindex < nrinfiles*/
1602     flushspus(0x7fffffff); /* ensure all remaining spus elements are output */
1603     fprintf(fdo, "\t</stream>\n</subpictures>\n");
1604     fclose(fdo);
1605     return 0;
1606   } /*main*/
1607