MythTV  master
pxsup2dast.c
Go to the documentation of this file.
1 /*
2  #
3  # pxsup2dast.c version YYYY-MM-DD (take from most recent change below).
4  #
5  # Project X sup to dvdauthor subtitle xml file.
6  # too ät iki piste fi
7  #
8  # -------------------------------------------------
9  #
10  # This is currently wery picky what this expects of ProjectX .sup to contain.
11  # Update: 2005/07/02 Not so picky anymore, but control sequence parsing is
12  # not perfect (yet!?)
13  #
14  # Change 2010-08-29: Initial handling of 0x07 control code (from
15  # Simon Liddicott). Currently just ignores contents but doesn't choke on
16  # it anymore...
17  #
18  # Change 2009-08-09: Renamed getline() as getpixelline() (to avoid function
19  # name collision. Fixed GPL version to 2 (only). Thanks Ville Skyttä.
20  #
21  # Change 2009-01-10: Added subtitle indexing change (fix) from patch
22  # sent by Ian Stewart.
23  #
24  # This program is released under GNU GPL version 2. Check
25  # http://www.fsf.org/licenses/licenses.html
26  # to get your own copy of the GPL v2 license.
27  #
28  */
29 
30 typedef enum { false = 0, true = 1 } bool;
31 typedef unsigned char bool8;
32 
33 
34 #include <unistd.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <time.h>
42 #include <errno.h>
43 #include <ctype.h>
44 #include <setjmp.h>
45 #include <zlib.h>
46 
47 #if 1
48 /* this is all speed, not so much portability -- using C99 features... */
49 #include <stdint.h>
50 
51 typedef int8_t ei8; typedef uint8_t eu8;
52 typedef int16_t ei16; typedef uint16_t eu16;
53 typedef int32_t ei32; typedef uint32_t eu32;
54 
55 typedef int_least8_t li8; typedef uint_least8_t lu8;
56 typedef int_least16_t li16; typedef uint_least16_t lu16;
57 typedef int_least32_t li32; typedef uint_least32_t lu32;
58 
59 typedef int_fast8_t fi8; typedef uint_fast8_t fu8;
60 typedef int_fast16_t fi16; typedef uint_fast16_t fu16;
61 typedef int_fast32_t fi32; typedef uint_fast32_t fu32;
62 #endif
63 
64 
65 #if (__GNUC__ >= 3)
66 #define GCCATTR_PRINTF(m, n) __attribute__ ((format (printf, m, n)))
67 #define GCCATTR_UNUSED __attribute ((unused))
68 #define GCCATTR_NORETURN __attribute ((noreturn))
69 #define GCCATTR_CONST __attribute ((const))
70 #else
71 #define GCCATTR_PRINTF(m, n)
72 #define GCCATTR_UNUSED
73 #define GCCATTR_NORETURN
74 #define GCCATTR_CONST
75 #endif
76 
77 /* use this only to cast quoted strings in function calls */
78 #define CUS (const unsigned char *)
79 
80 /* forward declarations */
81 typedef struct _Png4File Png4File;
82 typedef struct _BoundStr BoundStr;
83 
85 
86 int sup2dast(const char *supfile, const char *ifofile, int delay_ms);
87 
88 /****** Poor man's exception code ... (heavily inspired by cexcept). ******/
89 
90 struct exc__state
91 {
92  struct exc__state * prev;
93  jmp_buf env;
94 };
95 
96 struct
97 {
98  struct exc__state * last;
99  char msgbuf[1024];
100  int buflen;
101 } EXC /* = { 0 }*/ ;
102 
103 // These macros now all take a parameter 'x' to specify a recursion
104 // level. This will then generate unique variable names for each
105 // level of recursion, preventing the compiler from complaining about
106 // shadowed variables.
107 #define exc_try(x) do { struct exc__state exc_s##x; int exc_type##x GCCATTR_UNUSED; \
108  exc_s##x.prev = EXC.last; EXC.last = &exc_s##x; if ((exc_type##x = setjmp(exc_s##x.env)) == 0)
109 
110 #define exc_ftry(x) do { struct exc__state exc_s##x, *exc_p##x = EXC.last; \
111  int exc_type##x GCCATTR_UNUSED; exc_s##x.prev = EXC.last; \
112  EXC.last = &exc_s##x; if ((exc_type##x = setjmp(exc_s##x.env)) == 0)
113 
114 #define exc_catch(x,t) else if ((t) == exc_type##x)
115 
116 #define exc_end(x) else __exc_throw(exc_type##x); EXC.last = exc_s##x.prev; } while (0)
117 
118 #define exc_return(x) for (EXC.last = exc_p##x;) return
119 
120 #define exc_fthrow(x) for (EXC.last = exc_p##x;) ex_throw
121 
122 #define exc_catchall else
123 #define exc_endall(x) EXC.last = exc_s##x.prev; } while (0)
124 
125 static void __exc_throw(int type) /* protoadd GCCATTR_NORETURN */
126 {
127  struct exc__state * exc_s;
128  exc_s = EXC.last;
129  EXC.last = EXC.last->prev;
130  longjmp(exc_s->env, type);
131 }
132 
133 static void exc_throw(int type, const char * format, ...)
134  /* protoadd GCCATTR_NORETURN */
135 {
136  if (format != NULL)
137  {
138  va_list ap;
139  unsigned int len;
140  int err = errno;
141 
142  va_start(ap, format);
143  len = vsnprintf(EXC.msgbuf, sizeof EXC.msgbuf, format, ap);
144  va_end(ap);
145 
146  if (len >= sizeof EXC.msgbuf)
147  {
148  len = sizeof EXC.msgbuf - 1;
149  EXC.msgbuf[len] = '\0';
150  }
151  else
152  {
153  if (format[strlen(format) - 1] == ':')
154  {
155  int l = snprintf(&EXC.msgbuf[len], sizeof EXC.msgbuf - len,
156  " %s.", strerror(err));
157  if (l + len >= sizeof EXC.msgbuf)
158  {
159  len = sizeof EXC.msgbuf - 1;
160  EXC.msgbuf[len] = '\0';
161  }
162  else
163  len += l;
164  }
165  }
166  EXC.buflen = len;
167  }
168  else
169  {
170  EXC.msgbuf[0] = '\0';
171  EXC.buflen = 0;
172  }
173 
174  __exc_throw(type);
175 }
176 
177 /****** end exception code block ******/
178 
179 
180 static eu8 * xxfread(FILE * stream, eu8 * ptr, size_t size)
181 {
182  size_t n = fread(ptr, size, 1, stream);
183  if (n == 0)
184  {
185  if (ferror(stream))
186  exc_throw(MiscError, "fread failure:");
188  return ptr;
189 }
190 
191 static void xxfwrite(FILE * stream, const eu8 * ptr, size_t size)
192 {
193  size_t n = fwrite(ptr, size, 1, stream);
194  if (n == 0)
195  {
196  if (ferror(stream))
197  exc_throw(MiscError, "fwrite failure:");
198  exc_throw(MiscError, "fwrite failure:");
199  }
200 }
201 
202 #define xxfwriteCS(f, s) xxfwrite(f, CUS s, sizeof (s) - 1)
203 
204 static void yuv2rgb(int y, int cr, int cb,
205  eu8 * r, eu8 * g, eu8 * b)
206 {
207  int lr, lg, lb;
208 
209  /* from dvdauthor... */
210  lr = (500 + 1164 * (y - 16) + 1596 * (cr - 128) ) /1000;
211  lg = (500 + 1164 * (y - 16) - 813 * (cr - 128) - 391 * (cb - 128)) / 1000;
212  lb = (500 + 1164 * (y - 16) + 2018 * (cb - 128)) / 1000;
213 
214  *r = (lr < 0)? 0: (lr > 255)? 255: (eu8)lr;
215  *g = (lg < 0)? 0: (lg > 255)? 255: (eu8)lg;
216  *b = (lb < 0)? 0: (lb > 255)? 255: (eu8)lb;
217 }
218 
219 static void rgb2yuv(eu8 r, eu8 g, eu8 b,
220  eu8 * y, eu8 * cr, eu8 * cb)
221 {
222  /* int ly, lcr, lcb; */
223 
224  /* from dvdauthor... */
225  *y = ( 257 * r + 504 * g + 98 * b + 16500) / 1000;
226  *cr = ( 439 * r - 368 * g - 71 * b + 128500) / 1000;
227  *cb = (-148 * r - 291 * g + 439 * b + 128500) / 1000;
228 }
229 
230 /* the code above matches nicely with http://www.fourcc.org/fccyvrgb.php
231 
232  * RGB to YUV Conversion
233 
234  * Y = (0.257 * R) + (0.504 * G) + (0.098 * B) + 16
235  * Cr = V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128
236  * Cb = U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128
237 
238  * YUV to RGB Conversion
239 
240  * B = 1.164(Y - 16) + 2.018(U - 128)
241  * G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)
242  * R = 1.164(Y - 16) + 1.596(V - 128)
243 
244 */
245 
246 
247 static FILE * xfopen(const char * filename, const char * mode)
248 {
249  FILE * fh = fopen(filename, mode);
250  if (fh == NULL)
251  exc_throw(MiscError, "fopen(\"%s\", \"%s\") failure:", filename, mode);
252  return fh;
253 }
254 
255 static void xfseek0(FILE * stream, long offset)
256 {
257  if (fseek(stream, offset, SEEK_SET) < 0)
258  exc_throw(MiscError, "fseek(stream, %ld, SEEK_SET) failure:",offset);
259 }
260 
261 static void xmkdir(const char * path, int mode)
262 {
263  if (mkdir(path, mode) < 0)
264  exc_throw(MiscError, "mkdir(%s, 0%o) failure:", path, mode);
265 }
266 
267 
268 static bool fexists(const char * filename)
269 {
270  struct stat st;
271 
272  if (stat(filename, &st) == 0 && S_ISREG(st.st_mode))
273  return true;
274  return false;
275 }
276 
277 static bool dexists(const char * filename)
278 {
279  struct stat st;
280 
281  if (stat(filename, &st) == 0 && S_ISDIR(st.st_mode))
282  return true;
283 
284  return false;
285 }
286 
287 static fu32 get_uint32_be(const eu8 * bytes)
288 {
289  return (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
290 }
291 
292 static fu16 get_uint16_be(const eu8 * bytes)
293 {
294  return (bytes[0] << 8) + bytes[1];
295 }
296 
297 static fu32 get_uint32_le(const eu8 * bytes)
298 {
299  return (bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0];
300 }
301 
302 #if 0 /* protoline */
303 static fu32 get_uint16_le(const eu8 * bytes) {
304  return (bytes[1] << 8) + bytes[0]; }
305 #endif /* protoline */
306 
307 
308 static void set_uint32_be(eu8 * ptr, eu32 value)
309 {
310  ptr[0] = value>>24; ptr[1] = value>>16; ptr[2] = value>>8; ptr[3] =value;
311 }
312 
313 #if 0 /* protoline */
314 static void set_uint16_be(eu8 * ptr, eu32 value) {
315  ptr[0] = value>>8; ptr[1] = value; }
316 
317 static void set_uint32_le(eu8 * ptr, eu32 value) {
318  ptr[3] = value>>24; ptr[2] = value>>16; ptr[1] = value>>8; ptr[0] =value; }
319 #endif /* protoline */
320 
321 static void set_uint16_le(eu8 * ptr, eu16 value)
322 {
323  ptr[1] = value>>8; ptr[0] =value;
324 }
325 
326 static void xxfwrite_uint32_be(FILE * fh, eu32 value)
327 {
328  eu8 buf[4];
329  set_uint32_be(buf, value);
330  xxfwrite(fh, buf, 4);
331 }
332 
333 static void ifopalette(const char * filename,
334  eu8 yuvpalette[16][3], eu8 rgbpalette[16][3])
335 {
336  eu8 buf[1024], r, g, b;
337  fu32 offset, pgc;
338  int i;
339  FILE * fh;
340 
341  fh = xfopen(filename, "rb");
342  if (memcmp(xxfread(fh, buf, 12), "DVDVIDEO-VTS", 12) != 0)
344  "(IFO) file %s not of type DVDVIDEO-VTS.", filename);
345 
346  xfseek0(fh, 0xcc);
347  offset = get_uint32_be(xxfread(fh, buf, 4));
348  xfseek0(fh, offset * 0x800 + 12);
349  pgc = offset * 0x800 + get_uint32_be(xxfread(fh, buf, 4));
350  /* seek to palette */
351  xfseek0(fh, pgc + 0xa4);
352  xxfread(fh, buf, 16 * 4);
353  fclose(fh);
354  for (i = 0; i < 16; i++)
355  {
356  eu8 * p = buf + i * 4 + 1;
357  yuvpalette[i][0] =p[0]; yuvpalette[i][1] =p[1]; yuvpalette[i][2] =p[2];
358  yuv2rgb(p[0], p[1], p[2], &r, &g, &b);
359  rgbpalette[i][0] = r; rgbpalette[i][1] = g; rgbpalette[i][2] = b;
360  }
361 }
362 
363 
364 static void set2palettes(int value, eu8 * yuvpalpart, eu8 * rgbpalpart)
365 {
366  eu8 r, g, b, y, cr, cb;
367 
368  r = value >> 16; g = value >> 8; b = value;
369  rgbpalpart[0] = r, rgbpalpart[1] = g, rgbpalpart[2] = b;
370  rgb2yuv(r, g, b, &y, &cr, &cb);
371  yuvpalpart[0] = y, yuvpalpart[1] = cr, yuvpalpart[2] = cb;
372 }
373 
374 
375 static void argpalette(const char * arg,
376  eu8 yuvpalette[16][3], eu8 rgbpalette[16][3])
377 {
378  unsigned int i;
379 
380  if (strlen(arg) != 20 || arg[6] != ',' || arg[13] != ',')
381  exc_throw(MiscError, "Palette arg %s invalid.\n", arg);
382 
383  for (i = 0; i < 16; i++)
384  set2palettes(i * 0x111111, yuvpalette[i], rgbpalette[i]);
385 
386  sscanf(arg, "%x", &i); /* "%x," ? */
387  set2palettes(i, yuvpalette[1], rgbpalette[1]);
388  sscanf(arg + 7, "%x", &i);
389  set2palettes(i, yuvpalette[2], rgbpalette[2]);
390  sscanf(arg + 14, "%x", &i);
391  set2palettes(i, yuvpalette[3], rgbpalette[3]);
392 }
393 
394 
395 /* typedef struct _Png4File Png4File; */
396 struct _Png4File
397 {
398  FILE * fh;
399  int width;
400  int hleft;
401  int nibble;
402  int bufpos;
403  int chunklen;
407  eu8 buffer[65536];
408 };
409 
410 static void png4file_init(Png4File * self, eu8 palette[4][3])
411 {
412  memcpy(self->palettechunk, "\0\0\0\x0c" "PLTE", 8);
413  memcpy(self->palettechunk + 8, palette, 12);
414  self->crc = 0 /*crc32(0, Z_NULL, 0)*/;
415  self->crc = crc32(self->crc, self->palettechunk + 4, 16);
416  set_uint32_be(self->palettechunk + 20, self->crc);
417 }
418 
419 static void png4file_open(Png4File * self,
420  const char * filename, int height, int width)
421 {
422  eu32 crc;
423  self->fh = xfopen(filename, "wb");
424  self->width = width;
425  self->hleft = height;
426  self->nibble = -1;
427 
428  xxfwrite(self->fh, CUS "\x89PNG\r\n\x1a\n" "\0\0\0\x0d", 12);
429 
430  memcpy(self->buffer, "IHDR", 4);
431  set_uint32_be(self->buffer + 4, width);
432  set_uint32_be(self->buffer + 8, height);
433  memcpy(self->buffer + 12, "\004\003\0\0\0", 5);
434 
435  crc = crc32(0, self->buffer, 17);
436  set_uint32_be(self->buffer + 17, crc);
437  xxfwrite(self->fh, self->buffer, 21);
438 
439  xxfwrite(self->fh, self->palettechunk, sizeof self->palettechunk);
440 
441  /* XXX quick hack, first color transparent. */
442  xxfwriteCS(self->fh, "\0\0\0\001" "tRNS" "\0" "\x40\xe6\xd8\x66");
443 
444  xxfwrite(self->fh, CUS "\0\0\0\0IDAT" "\x78\001", 10);
445  self->buffer[0] = '\0';
446  self->buffer[5] = '\0';
447  self->bufpos = 6;
448  self->chunklen = 2; /* 78 01, zlib header */
449  self->crc = crc32(0, CUS "IDAT" "\x78\001", 6);
450  self->adler = 1 /* adler32(0, Z_NULL, 0) */;
451 }
452 
453 static void png4file_flush(Png4File * self)
454 {
455  int l = self->bufpos - 5;
456  self->chunklen += self->bufpos;
457  set_uint16_le(self->buffer + 1, l);
458  set_uint16_le(self->buffer + 3, l ^ 0xffff);
459  xxfwrite(self->fh, self->buffer, self->bufpos);
460  self->crc = crc32(self->crc, self->buffer, self->bufpos);
461  self->adler = adler32(self->adler, self->buffer + 5, self->bufpos - 5);
462  self->buffer[0] = '\0';
463  self->bufpos = 5;
464 }
465 
466 static void png4file_addpixel(Png4File * self, eu8 pixel)
467 {
468  if (self->nibble < 0)
469  self->nibble = (pixel << 4);
470  else
471  {
472  self->buffer[self->bufpos++] = self->nibble | pixel;
473  self->nibble = -1;
474  if (self->bufpos == sizeof self->buffer - 4)
475  png4file_flush(self);
476  }
477 }
478 
479 static void png4file_endrow(Png4File * self)
480 {
481  if (self->nibble >= 0)
482  {
483  self->buffer[self->bufpos++] = self->nibble;
484  self->nibble = -1;
485  }
486 
487  self->hleft--;
488  if (self->hleft)
489  self->buffer[self->bufpos++] = '\0';
490 }
491 
492 static void png4file_close(Png4File * self)
493 {
494  eu8 adlerbuf[4];
495  self->buffer[0] = 0x01;
496  if (self->bufpos)
497  png4file_flush(self);
498  else
499  {
500  self->bufpos = 5;
501  png4file_flush(self);
502  }
503 
504  set_uint32_be(adlerbuf, self->adler);
505  xxfwrite(self->fh, adlerbuf, 4);
506  self->crc = crc32(self->crc, adlerbuf, 4);
507  xxfwrite_uint32_be(self->fh, self->crc);
508  xxfwriteCS(self->fh, "\0\0\0\0" "IEND" "\xae\x42\x60\x82");
509  xfseek0(self->fh, 70);
510  xxfwrite_uint32_be(self->fh, self->chunklen + 4 /* adler*/);
511  fclose(self->fh);
512  self->fh = NULL;
513 }
514 
515 static eu8 getnibble(eu8 ** data, int * nibble)
516 {
517  if (*nibble >= 0)
518  {
519  eu8 rv = *nibble & 0x0f;
520  *nibble = -1;
521  return rv;
522  }
523  /* else */
524  *nibble = *(*data)++;
525  return *nibble >> 4;
526 }
527 
528 
529 static void getpixelline(eu8 ** data, int width, Png4File * picfile)
530 {
531  int nibble = -1;
532  int col = 0;
533  int number, cindex;
534  /* Originally from gtkspu - this from the python implementation of this */
535 
536  while (1)
537  {
538  int bits = getnibble(data, &nibble);
539  if ((bits & 0xc) != 0)
540  {
541  /* have 4-bit code */
542  number = (bits & 0xc) >> 2;
543  cindex = bits & 0x3; }
544  else
545  {
546  bits = (bits << 4) | getnibble(data, &nibble);
547  if ((bits & 0xf0) != 0)
548  {
549  /* have 8-bit code */
550  number = (bits & 0x3c) >> 2;
551  cindex = bits & 0x3;
552  }
553  else
554  {
555  bits = (bits << 4) | getnibble(data, &nibble);
556  if ((bits & 0xfc0) != 0)
557  {
558  /* have 12-bit code */
559  number = (bits & 0xfc) >> 2;
560  cindex = bits & 0x3;
561  }
562  else
563  {
564  /* have 16-bit code */
565  bits = (bits << 4) | getnibble(data, &nibble);
566  number = (bits & 0x3fc) >> 2;
567  cindex = bits & 0x3;
568 
569  if (number == 0)
570  number = width;
571  }
572  }
573  }
574 
575  /* printf("%d %d %d %d\n", number, col, width, cindex); */
576  for (; number > 0 && col < width; number--, col++)
577  png4file_addpixel(picfile, cindex);
578 
579  if (col == width)
580  {
581  png4file_endrow(picfile);
582  return;
583  }
584  }
585 }
586 
587 static void makebitmap(eu8 * data, int w, int h, int top, int bot,
588  char * filename, eu8 palette[4][3])
589 {
590  eu8 * top_ibuf = data + top;
591  eu8 * bot_ibuf = data + bot;
592  int i;
593  Png4File picfile;
594 
595  png4file_init(&picfile, palette); /* not bottleneck even re-doing this every time */
596  png4file_open(&picfile, filename, h, w);
597  for (i = 0; i < h / 2; i++)
598  {
599  getpixelline(&top_ibuf, w, &picfile);
600  getpixelline(&bot_ibuf, w, &picfile);
601  }
602 
603  png4file_close(&picfile);
604 }
605 
606 
607 static char * pts2ts(fu32 pts, char * rvbuf, bool is_png_filename)
608 {
609  int h = pts / (3600 * 90000);
610  int m = pts / (60 * 90000) % 60;
611  int s = pts / 90000 % 60;
612  int hs = (pts + 450) / 900 % 100;
613 
614  if (is_png_filename)
615  sprintf(rvbuf, "%02d+%02d+%02d.%02d.png", h, m, s, hs);
616  else
617  sprintf(rvbuf, "%02d:%02d:%02d.%02d", h, m, s, hs);
618 
619  return rvbuf;
620 }
621 
622 /* *********** */
623 
624 struct _BoundStr
625 {
626  eu8 * p;
627  int l;
628 };
629 
630 static void boundstr_init(BoundStr * bs, eu8 * p, int l)
631 {
632  bs->p = p;
633  bs->l = l;
634 }
635 
636 static eu8 * boundstr_read(BoundStr * bs, int l)
637 {
638  eu8 * rp;
639  if (l > bs->l)
640  exc_throw(IndexError, "XXX IndexError %p.", bs);
641  rp = bs->p;
642  bs->p += l;
643  bs->l -= l;
644  return rp;
645 }
646 
647 /* *********** */
648 
649 
650 static void pxsubtitle(const char * supfile, FILE * ofh, eu8 palette[16][3],
651  bool createpics, int delay_ms,
652  char * fnbuf, char * fnbuf_fp)
653 {
654  char junk[32];
655  char sptsstr[32];
656  eu8 data[65536];
657  eu8 * ctrl;
658  time_t volatile pt = 0;
659  bool volatile last = false;
660  /*char transparent[8]; */
661  FILE * sfh = xfopen(supfile, "rb");
662  if (memcmp(xxfread(sfh, data, 2), "SP", 2) != 0)
663  {
664  exc_throw(MiscError, "Syncword missing. XXX bailing out.");
665  }
666 
667  /*sprintf(transparent,
668  "%02x%02x%02x", palette[0][0], palette[0][1], palette[0][2]); */
669 
670  exc_try(1)
671  {
672  eu32 volatile lastendpts = 0;
673 
674  while (1)
675  {
676  /* X.java reads 5 bytes of pts, SubPicture.java writes 4. With
677  * 4 bytes 47721 seconds (13.25 hours) can be handled.
678  * Later, bytes 1,2,3,4 (instead of 0,1,2,3) could be used.
679  * 256/90000 = 1/351 sec -- way better than required resolution.
680  */
681  eu32 volatile pts = get_uint32_le(xxfread(sfh, data, 8));
682  eu16 size = get_uint16_be(xxfread(sfh, data, 2));
683  eu16 pack = get_uint16_be(xxfread(sfh, data, 2));
684  eu32 endpts;
685  xxfread(sfh, data, pack - 4);
686  ctrl = data + pack - 4;
687  xxfread(sfh, ctrl, size - pack);
688 
689  exc_try(2)
690  {
691  if (memcmp(xxfread(sfh, (unsigned char *)junk, 2),
692  "SP", 2) != 0)
693  exc_throw(MiscError, "Syncword missing. XXX bailing out.");
694  }
696  last = true;
697  exc_end(2);
698  {
699  time_t ct;
700  BoundStr bs;
701  int prev;
702  int x1 = -1, x2 = -1, y1 = -1, y2 = -1;
703  int top_field = -1, bot_field = -1;
704  int end = 0;
705  int colcon_length;
706  eu8 this_palette[4][3];
707  boundstr_init(&bs, ctrl, size - pack);
708 
709  prev = 0;
710  while (1)
711  {
712  int date = get_uint16_be(boundstr_read(&bs, 2));
713  int next = get_uint16_be(boundstr_read(&bs, 2));
714 
715  while (1)
716  {
717  eu8 * p;
718  eu8 cmd = boundstr_read(&bs, 1)[0];
719  int xpalette, i, n;
720  switch (cmd)
721  {
722  case 0x00: /* force display: */
723  continue;
724  case 0x01: /* start date (read above) */
725  continue;
726  case 0x02: /* stop date (read above) */
727  end = date;
728  continue;
729  case 0x03: /* palette */
730  xpalette = get_uint16_be(boundstr_read(&bs, 2));
731  for (n = 0; n < 4; n++) {
732  i = (xpalette >> (n * 4) & 0x0f);
733  this_palette[n][0] = palette[i][0];
734  this_palette[n][1] = palette[i][1];
735  this_palette[n][2] = palette[i][2];
736  }
737  continue;
738  case 0x04: /* alpha channel */
739  /*alpha =*/ boundstr_read(&bs, 2);
740  continue;
741  case 0x05: /* coordinates */
742  p = boundstr_read(&bs, 6);
743  x1 = (p[0] << 4) + (p[1] >> 4);
744  x2 = ((p[1] & 0xf) << 8) + p[2];
745  y1 = (p[3] << 4) + (p[4] >> 4);
746  y2 = ((p[4] & 0xf) << 8) + p[5];
747  continue;
748  case 0x06: /* rle offsets */
749  top_field = get_uint16_be(boundstr_read(&bs, 2));
750  bot_field = get_uint16_be(boundstr_read(&bs, 2));
751  continue;
752  case 0x07: /* */
753  colcon_length = get_uint16_be(boundstr_read(&bs, 2)-2);
754  boundstr_read(&bs, colcon_length);
755  continue;
756  }
757  if (cmd == 0xff) /* end command */
758  break;
759  exc_throw(MiscError, "%d: Unknown control sequence", cmd);
760  }
761 
762  if (prev == next)
763  break;
764  prev = next;
765  }
766 
767  /* check for overlapping subtitles */
768  eu32 tmppts = pts;
769  if (delay_ms)
770  tmppts += delay_ms * 90;
771 
772  /* hack to fix projectx bug adding wrong end times around a cut point*/
773  if (end > 500)
774  end = 500;
775 
776  endpts = tmppts + end * 1000; /* ProjectX ! (other: 900, 1024) */
777 
778  if (tmppts <= lastendpts)
779  {
780  if (lastendpts < endpts)
781  {
782  pts = lastendpts + 2 * (1000 / 30) * 90;/*??? */
783  tmppts = pts;
784  if (delay_ms)
785  tmppts += delay_ms * 90;
786  printf("Fixed overlapping subtitle!\n");
787  }
788  else
789  {
790  printf("Dropping overlapping subtitle\n");
791  continue;
792  }
793  }
794 
795  lastendpts = endpts;
796 
797  pts2ts(pts, fnbuf_fp, true);
798  pts2ts(tmppts, sptsstr + 1, false);
799 
800  if (pt != time(&ct))
801  {
802  pt = ct;
803  sptsstr[0] = '\r';
804  size_t len = write(1, sptsstr, strlen(sptsstr));
805  if (len != strlen(sptsstr))
806  printf("ERROR: write failed");
807  }
808 
809  fprintf(ofh, " <spu start=\"%s\" end=\"%s\" image=\"%s\""
810  " xoffset=\"%d\" yoffset=\"%d\" />\n",
811  sptsstr + 1, pts2ts(endpts, junk, false),
812  fnbuf, x1, y1);
813 
814  if (createpics)
815  makebitmap(data, x2 - x1 + 1, y2 - y1 + 1, top_field - 4,
816  bot_field - 4, fnbuf, this_palette);
817  if (last)
819  }
820  }
821  }
823  {
824  size_t len = write(1, sptsstr, strlen(sptsstr));
825  if (len != strlen(sptsstr))
826  printf("ERROR: write failed");
827  return;
828  }
829  exc_end(1);
830 }
831 
832 #if 0
833 static void usage(const char * pn) /* protoadd GCCATTR_NORETURN */
834 {
835  exc_throw(MiscError, "\n"
836  "Usage: %s [--delay ms] <supfile> <ifofile>|<palette>" "\n"
837  "\n"
838  "\tExamples:" "\n"
839  "\n"
840  "\t ProjectX decoded recording.sup and recording.sup.IFO" "\n"
841  "\n"
842  "\t\t$ pxsup2dast recording.sup*" "\n"
843  "\n"
844  "\t Having test.sup and map.ifo" "\n"
845  "\n"
846  "\t\t$ pxsup2dast test.sup map.ifo" "\n"
847  "\n"
848  "\t No .IFO, so giving 3 colors (rgb components in hex)" "\n"
849  "\n"
850  "\t\t$ pxsup2dast titles.sup ff0000,00ff00,0000ff" "\n"
851  "\n"
852  "\t Trying to fix sync in recording" "\n"
853  "\n"
854  "\t\t$ pxsup2dast --delay 750 recording.sup*" "\n"
855  , pn);
856 }
857 #endif
858 
859 static bool samepalette(char * filename, eu8 palette[16][3])
860 {
861  FILE * fh;
862  int i;
863  unsigned int r,g,b;
864 
865  if (!fexists(filename))
866  return false;
867 
868  fh = xfopen(filename, "rb");
869  for (i = 0; i < 16; i++)
870  {
871  if (fscanf(fh, "%02x%02x%02x\n", &r, &g, &b) != 3 ||
872  r != palette[i][0] || g != palette[i][1] || b != palette[i][2])
873  {
874  fclose(fh);
875  return false;
876  }
877  }
878  fclose(fh);
879  return true;
880 }
881 
882 int sup2dast(const char *supfile, const char *ifofile ,int delay_ms)
883 {
884  exc_try(1)
885  {
886  int i;
887  eu8 yuvpalette[16][3], rgbpalette[16][3];
888  char fnbuf[1024];
889  char * p;
890 
891  bool createpics;
892  FILE * fh;
893 
894  memset(yuvpalette, 0, sizeof(yuvpalette));
895  memset(rgbpalette, 0, sizeof(rgbpalette));
896 
897  if (write(1, "\n", 1) != 1)
898  printf("ERROR: write failed");
899 
900  if (sizeof (char) != 1 || sizeof (int) < 2) /* very unlikely */
901  exc_throw(MiscError, "Incompatible variable sizes.");
902 
903  if ((p = strrchr(supfile, '.')) != NULL)
904  i = p - supfile;
905  else
906  i = strlen(supfile);
907  if (i > 950)
908  exc_throw(MiscError, "Can "
909  "not manage filenames longer than 950 characters.");
910  memcpy(fnbuf, supfile, i);
911  p = fnbuf + i;
912  strcpy(p, ".d/");
913  p += strlen(p);
914 
915  if (!dexists(fnbuf))
916  xmkdir(fnbuf, 0755);
917 
918  if (fexists(ifofile))
919  ifopalette(ifofile, yuvpalette, rgbpalette);
920  else
921  argpalette(ifofile, yuvpalette, rgbpalette);
922 
923  strcpy(p, "palette.ycrcb");
924  if (samepalette(fnbuf, yuvpalette))
925  {
926  createpics = false;
927  printf("Found palette.yuv having our palette information...\n");
928  printf("...Skipping image files, Writing spumux.tmp.\n");
929  }
930  else
931  {
932  createpics = true;
933  printf("Writing image files and spumux.tmp.\n");
934  }
935 
936  strcpy(p, "spumux.tmp");
937  fh = xfopen(fnbuf, "wb");
938 
939  xxfwriteCS(fh, "<subpictures>\n <stream>\n");
940  pxsubtitle(supfile, fh, rgbpalette, createpics, delay_ms, fnbuf, p);
941 
942  if (write(1, "\n", 1) != 1)
943  printf("ERROR: write failed");
944 
945  xxfwriteCS(fh, " </stream>\n</subpictures>\n");
946  fclose(fh);
947 
948  if (createpics)
949  {
950  FILE * yuvfh, *rgbfh;
951  printf("Writing palette.ycrcb and palette.rgb.\n");
952  strcpy(p, "palette.ycrcb");
953  yuvfh = xfopen(fnbuf, "wb");
954  strcpy(p, "palette.rgb");
955  rgbfh = xfopen(fnbuf, "wb");
956  for (i = 0; i < 16; i++)
957  {
958  fprintf(yuvfh, "%02x%02x%02x\n",
959  yuvpalette[i][0], yuvpalette[i][1], yuvpalette[i][2]);
960  fprintf(rgbfh, "%02x%02x%02x\n",
961  rgbpalette[i][0], rgbpalette[i][1], rgbpalette[i][2]);
962  }
963  fclose(yuvfh);
964  fclose(rgbfh);
965  }
966 
967  {
968  char buf[1024];
969  printf("Renaming spumux.tmp to spumux.xml.\n");
970  strcpy(p, "spumux.tmp");
971  i = strlen(fnbuf);
972  memcpy(buf, fnbuf, i);
973  strcpy(&buf[i - 3], "xml");
974  unlink(buf);
975  rename(fnbuf, buf);
976  }
977  p[0] = '\0';
978  printf("Output files reside in %s\n", fnbuf);
979  printf("All done.\n");
980  }
981 
982  exc_catchall
983  {
984  if (write(2, EXC.msgbuf, EXC.buflen) != EXC.buflen)
985  printf("ERROR: write failed");
986 
987  if (write(2, "\n", 1) != 1)
988  printf("ERROR: write failed");
989 
990  return 1;
991  }
992  exc_endall(1);
993 
994  return 0;
995 }
uint8_t eu8
Definition: pxsup2dast.c:51
int_least8_t li8
Definition: pxsup2dast.c:55
def write(text, progress=True)
Definition: mythburn.py:279
#define CUS
Definition: pxsup2dast.c:78
struct exc__state * prev
Definition: pxsup2dast.c:92
uint32_t eu32
Definition: pxsup2dast.c:53
struct exc__state * last
Definition: pxsup2dast.c:98
eu32 crc
Definition: pxsup2dast.c:404
static eu8 * boundstr_read(BoundStr *bs, int l)
Definition: pxsup2dast.c:636
uint_least32_t lu32
Definition: pxsup2dast.c:57
#define NULL
Definition: H264Parser.h:62
static void png4file_open(Png4File *self, const char *filename, int height, int width)
Definition: pxsup2dast.c:419
int8_t ei8
Definition: pxsup2dast.c:51
uint_fast32_t fu32
Definition: pxsup2dast.c:61
uint_least8_t lu8
Definition: pxsup2dast.c:55
#define exc_try(x)
Definition: pxsup2dast.c:107
static void png4file_close(Png4File *self)
Definition: pxsup2dast.c:492
static void boundstr_init(BoundStr *bs, eu8 *p, int l)
Definition: pxsup2dast.c:630
int buflen
Definition: pxsup2dast.c:100
static void xmkdir(const char *path, int mode)
Definition: pxsup2dast.c:261
static FILE * xfopen(const char *filename, const char *mode)
Definition: pxsup2dast.c:247
bool
Definition: pxsup2dast.c:30
eu8 buffer[65536]
Definition: pxsup2dast.c:407
static fu16 get_uint16_be(const eu8 *bytes)
Definition: pxsup2dast.c:292
static void set_uint16_le(eu8 *ptr, eu16 value)
Definition: pxsup2dast.c:321
static void xxfwrite(FILE *stream, const eu8 *ptr, size_t size)
Definition: pxsup2dast.c:191
static eu8 getnibble(eu8 **data, int *nibble)
Definition: pxsup2dast.c:515
#define exc_catchall
Definition: pxsup2dast.c:122
int32_t ei32
Definition: pxsup2dast.c:53
#define exc_catch(x, t)
Definition: pxsup2dast.c:114
int_fast16_t fi16
Definition: pxsup2dast.c:60
int16_t ei16
Definition: pxsup2dast.c:52
static void set_uint32_be(eu8 *ptr, eu32 value)
Definition: pxsup2dast.c:308
unsigned char r
Definition: ParseText.cpp:329
unsigned char b
Definition: ParseText.cpp:329
static void png4file_init(Png4File *self, eu8 palette[4][3])
Definition: pxsup2dast.c:410
static void png4file_flush(Png4File *self)
Definition: pxsup2dast.c:453
jmp_buf env
Definition: pxsup2dast.c:93
static fu32 get_uint32_be(const eu8 *bytes)
Definition: pxsup2dast.c:287
int_fast8_t fi8
Definition: pxsup2dast.c:59
static void ifopalette(const char *filename, eu8 yuvpalette[16][3], eu8 rgbpalette[16][3])
Definition: pxsup2dast.c:333
int chunklen
Definition: pxsup2dast.c:403
uint_fast16_t fu16
Definition: pxsup2dast.c:60
FILE * fh
Definition: pxsup2dast.c:398
static int x2
Definition: mythsocket.cpp:61
static char * pts2ts(fu32 pts, char *rvbuf, bool is_png_filename)
Definition: pxsup2dast.c:607
static void pxsubtitle(const char *supfile, FILE *ofh, eu8 palette[16][3], bool createpics, int delay_ms, char *fnbuf, char *fnbuf_fp)
Definition: pxsup2dast.c:650
static void xfseek0(FILE *stream, long offset)
Definition: pxsup2dast.c:255
#define exc_endall(x)
Definition: pxsup2dast.c:123
uint16_t eu16
Definition: pxsup2dast.c:52
int_least16_t li16
Definition: pxsup2dast.c:56
static void usage(char *progname)
Definition: replex.c:2385
static void yuv2rgb(int y, int cr, int cb, eu8 *r, eu8 *g, eu8 *b)
Definition: pxsup2dast.c:204
static void rgb2yuv(eu8 r, eu8 g, eu8 b, eu8 *y, eu8 *cr, eu8 *cb)
Definition: pxsup2dast.c:219
static void makebitmap(eu8 *data, int w, int h, int top, int bot, char *filename, eu8 palette[4][3])
Definition: pxsup2dast.c:587
unsigned short uint16_t
Definition: iso6937tables.h:1
uint_least16_t lu16
Definition: pxsup2dast.c:56
static void set2palettes(int value, eu8 *yuvpalpart, eu8 *rgbpalpart)
Definition: pxsup2dast.c:364
static bool samepalette(char *filename, eu8 palette[16][3])
Definition: pxsup2dast.c:859
static bool fexists(const char *filename)
Definition: pxsup2dast.c:268
int FILE
Definition: mythburn.py:110
int_least32_t li32
Definition: pxsup2dast.c:57
char msgbuf[1024]
Definition: pxsup2dast.c:99
static void argpalette(const char *arg, eu8 yuvpalette[16][3], eu8 rgbpalette[16][3])
Definition: pxsup2dast.c:375
static void exc_throw(int type, const char *format,...)
Definition: pxsup2dast.c:133
#define exc_end(x)
Definition: pxsup2dast.c:116
static void getpixelline(eu8 **data, int width, Png4File *picfile)
Definition: pxsup2dast.c:529
eu8 * p
Definition: pxsup2dast.c:626
#define xxfwriteCS(f, s)
Definition: pxsup2dast.c:202
PictureAttribute next(PictureAttributeSupported supported, PictureAttribute attribute)
static void __exc_throw(int type)
Definition: pxsup2dast.c:125
eu8 palettechunk[24]
Definition: pxsup2dast.c:406
static void xxfwrite_uint32_be(FILE *fh, eu32 value)
Definition: pxsup2dast.c:326
struct @83 EXC
int bufpos
Definition: pxsup2dast.c:402
static guint32 * pixel
--------------------------------------------------—**
Definition: goom_core.c:33
eu32 adler
Definition: pxsup2dast.c:405
unsigned char bool8
Definition: pxsup2dast.c:31
static fu32 get_uint32_le(const eu8 *bytes)
Definition: pxsup2dast.c:297
static bool dexists(const char *filename)
Definition: pxsup2dast.c:277
int sup2dast(const char *supfile, const char *ifofile, int delay_ms)
Definition: pxsup2dast.c:882
static void png4file_endrow(Png4File *self)
Definition: pxsup2dast.c:479
int nibble
Definition: pxsup2dast.c:401
static eu8 * xxfread(FILE *stream, eu8 *ptr, size_t size)
Definition: pxsup2dast.c:180
int_fast32_t fi32
Definition: pxsup2dast.c:61
static uint32_t crc32(const unsigned char *data, int len)
Definition: dsmcc.cpp:618
uint_fast8_t fu8
Definition: pxsup2dast.c:59
static void png4file_addpixel(Png4File *self, eu8 pixel)
Definition: pxsup2dast.c:466
static int x1
Definition: mythsocket.cpp:60
unsigned char g
Definition: ParseText.cpp:329