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