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
31typedef enum { false = 0, true = 1 } bool;
32#endif
33typedef 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
57typedef int8_t ei8; typedef uint8_t eu8;
58typedef int16_t ei16; typedef uint16_t eu16;
59typedef int32_t ei32; typedef uint32_t eu32;
60
61typedef int_least8_t li8; typedef uint_least8_t lu8;
62typedef int_least16_t li16; typedef uint_least16_t lu16;
63typedef int_least32_t li32; typedef uint_least32_t lu32;
64
65typedef int_fast8_t fi8; typedef uint_fast8_t fu8;
66typedef int_fast16_t fi16; typedef uint_fast16_t fu16;
67typedef 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
81int sup2dast(const char *supfile, const char *ifofile, int delay_ms);
82
83/****** Poor man's exception code ... (heavily inspired by cexcept). ******/
84
86{
88 jmp_buf m_env;
89};
90
91struct
92{
94 char m_msgbuf[1024];
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
135GCCATTR_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
177}
178
179/****** end exception code block ******/
180
181
182static 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
193static 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
206static 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
215static 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
228static 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
256static 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
264static 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
270static 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
283static 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
292static 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
302static 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
311{
312 return ((fu16)(bytes[0]) << 8) +
313 (fu16)(bytes[1]);
314}
315
316static 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 */
325static fu32 get_uint16_le(const eu8 * bytes) {
326 return ((fu16)(bytes[1]) << 8) +
327 (fu16)(bytes[0]); }
328#endif /* protoline */
329
330
331static 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 */
337static void set_uint16_be(eu8 * ptr, eu32 value) {
338 ptr[0] = value>>8; ptr[1] = value; }
339
340static 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
344static void set_uint16_le(eu8 * ptr, eu16 value)
345{
346 ptr[1] = value>>8; ptr[0] =value;
347}
348
349static 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
356static 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
387static 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
401static 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
421typedef struct Png4File
422{
432 eu8 m_buffer[65536];
434
435static 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
444static 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
477static 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
490static 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
503static 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
516static 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
539static 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
553static 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
612static 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
636static 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
653typedef struct BoundStr
654{
656 int m_l;
658
659static void boundstr_init(BoundStr * bs, eu8 * p, int l)
660{
661 bs->m_p = p;
662 bs->m_l = l;
663}
664
665static 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
678static 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
863GCCATTR_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
889static 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
911int 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
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}
static uint32_t crc32(const unsigned char *data, int len)
Definition: dsmcc.cpp:617
static guint32 * pixel
--------------------------------------------------—**
Definition: goom_core.cpp:24
unsigned short uint16_t
Definition: iso6937tables.h:3
static int x1
Definition: mythsocket.cpp:50
static int x2
Definition: mythsocket.cpp:51
std::chrono::duration< CHRONO_TYPE, std::ratio< 1, 90000 > > pts
Definition: mythchrono.h:55
def write(text, progress=True)
Definition: mythburn.py:307
int FILE
Definition: mythburn.py:138
static void set_uint32_be(eu8 *ptr, eu32 value)
Definition: pxsup2dast.c:331
static char * pts2ts(fu32 pts, char *rvbuf, bool is_png_filename)
Definition: pxsup2dast.c:636
int16_t ei16
Definition: pxsup2dast.c:58
static bool fexists(const char *filename)
Definition: pxsup2dast.c:283
static fu32 get_uint32_le(const eu8 *bytes)
Definition: pxsup2dast.c:316
uint_fast32_t fu32
Definition: pxsup2dast.c:67
int_least32_t li32
Definition: pxsup2dast.c:63
int_fast16_t fi16
Definition: pxsup2dast.c:66
uint8_t eu8
Definition: pxsup2dast.c:57
static GCCATTR_NORETURN void __exc_throw(int type)
Definition: pxsup2dast.c:128
int_fast32_t fi32
Definition: pxsup2dast.c:67
int_least16_t li16
Definition: pxsup2dast.c:62
static eu8 * boundstr_read(BoundStr *bs, int l)
Definition: pxsup2dast.c:665
static void png4file_close(Png4File *self)
Definition: pxsup2dast.c:516
#define xxfwriteCS(f, s)
Definition: pxsup2dast.c:204
int_least8_t li8
Definition: pxsup2dast.c:61
struct BoundStr BoundStr
struct Png4File Png4File
struct exc__state * m_last
Definition: pxsup2dast.c:93
static bool samepalette(char *filename, eu8 palette[16][3])
Definition: pxsup2dast.c:889
#define exc_end(x)
Definition: pxsup2dast.c:117
static void yuv2rgb(int y, int cr, int cb, eu8 *r, eu8 *g, eu8 *b)
Definition: pxsup2dast.c:215
static void xxfwrite(FILE *stream, const eu8 *ptr, size_t size)
Definition: pxsup2dast.c:193
#define exc_catch(x, t)
Definition: pxsup2dast.c:115
#define CUS
Definition: pxsup2dast.c:77
static void png4file_init(Png4File *self, eu8 palette[4][3])
Definition: pxsup2dast.c:435
#define exc_try(x)
Definition: pxsup2dast.c:102
static void ifopalette(const char *filename, eu8 yuvpalette[16][3], eu8 rgbpalette[16][3])
Definition: pxsup2dast.c:356
int_fast8_t fi8
Definition: pxsup2dast.c:65
static void png4file_open(Png4File *self, const char *filename, int height, int width)
Definition: pxsup2dast.c:444
static void boundstr_init(BoundStr *bs, eu8 *p, int l)
Definition: pxsup2dast.c:659
uint_least32_t lu32
Definition: pxsup2dast.c:63
static fu32 get_uint32_be(const eu8 *bytes)
Definition: pxsup2dast.c:302
static void rgb2yuv(eu8 r, eu8 g, eu8 b, eu8 *y, eu8 *cr, eu8 *cb)
Definition: pxsup2dast.c:228
static bool dexists(const char *filename)
Definition: pxsup2dast.c:292
uint_least8_t lu8
Definition: pxsup2dast.c:61
static void set2palettes(int value, eu8 *yuvpalpart, eu8 *rgbpalpart)
Definition: pxsup2dast.c:387
static void argpalette(const char *arg, eu8 yuvpalette[16][3], eu8 rgbpalette[16][3])
Definition: pxsup2dast.c:401
static eu8 getnibble(eu8 **data, int *nibble)
Definition: pxsup2dast.c:539
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
static void xfseek0(FILE *stream, long offset)
Definition: pxsup2dast.c:264
uint_least16_t lu16
Definition: pxsup2dast.c:62
@ EOFIndicator
Definition: pxsup2dast.c:79
@ MiscError
Definition: pxsup2dast.c:79
@ IndexError
Definition: pxsup2dast.c:79
static GCCATTR_NORETURN void exc_throw(int type, const char *format,...)
Definition: pxsup2dast.c:135
static void png4file_flush(Png4File *self)
Definition: pxsup2dast.c:477
static void makebitmap(eu8 *data, int w, int h, int top, int bot, char *filename, eu8 palette[4][3])
Definition: pxsup2dast.c:612
static void xmkdir(const char *path, int mode)
Definition: pxsup2dast.c:270
#define exc_cleanup()
Definition: pxsup2dast.c:126
#define exc_catchall
Definition: pxsup2dast.c:123
uint32_t eu32
Definition: pxsup2dast.c:59
uint16_t eu16
Definition: pxsup2dast.c:58
int m_buflen
Definition: pxsup2dast.c:95
unsigned char bool8
Definition: pxsup2dast.c:33
int sup2dast(const char *supfile, const char *ifofile, int delay_ms)
Definition: pxsup2dast.c:911
#define GCCATTR_NORETURN
Definition: pxsup2dast.c:73
static void png4file_endrow(Png4File *self)
Definition: pxsup2dast.c:503
#define exc_endall(x)
Definition: pxsup2dast.c:124
static eu8 * xxfread(FILE *stream, eu8 *ptr, size_t size)
Definition: pxsup2dast.c:182
static FILE * xfopen(const char *filename, const char *mode)
Definition: pxsup2dast.c:256
static void xxfwrite_uint32_be(FILE *fh, eu32 value)
Definition: pxsup2dast.c:349
char m_msgbuf[1024]
Definition: pxsup2dast.c:94
int8_t ei8
Definition: pxsup2dast.c:57
uint_fast16_t fu16
Definition: pxsup2dast.c:66
static void getpixelline(eu8 **data, int width, Png4File *picfile)
Definition: pxsup2dast.c:553
static void set_uint16_le(eu8 *ptr, eu16 value)
Definition: pxsup2dast.c:344
bool
Definition: pxsup2dast.c:31
static fu16 get_uint16_be(const eu8 *bytes)
Definition: pxsup2dast.c:310
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:206
int32_t ei32
Definition: pxsup2dast.c:59
uint_fast8_t fu8
Definition: pxsup2dast.c:65
static void png4file_addpixel(Png4File *self, eu8 pixel)
Definition: pxsup2dast.c:490
struct @82 EXC
static void usage(char *progname)
Definition: replex.cpp:2415
eu8 * m_p
Definition: pxsup2dast.c:655
int m_nibble
Definition: pxsup2dast.c:426
eu8 m_paletteChunk[24]
Definition: pxsup2dast.c:431
FILE * m_fh
Definition: pxsup2dast.c:423
int m_chunkLen
Definition: pxsup2dast.c:428
eu32 m_adler
Definition: pxsup2dast.c:430
int m_hleft
Definition: pxsup2dast.c:425
eu32 m_crc
Definition: pxsup2dast.c:429
eu8 m_buffer[65536]
Definition: pxsup2dast.c:432
int m_bufPos
Definition: pxsup2dast.c:427
int m_width
Definition: pxsup2dast.c:424
struct exc__state * m_prev
Definition: pxsup2dast.c:87
jmp_buf m_env
Definition: pxsup2dast.c:88