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