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