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