2 * From ccextractor, leave this file as intact and close to the original as possible so that
3 * it is easy to patch in fixes - even though this file contains code that we don't need.
5 * Note that the SRT sub generation from CC could be useful for mkv subs.
8 #include "deccc608sub.h"
11 * ccextractor static configuration variables.
13 static int debug_608 = 0;
14 static int trim_subs = 0;
15 static int nofontcolor = 0;
16 static enum encoding_type encoding = ENC_UTF_8;
17 static int cc_channel = 1;
18 static enum output_format write_format = OF_SRT;
19 static int sentence_cap = 1;
20 static int subs_delay = 0;
21 static LLONG screens_to_process = -1;
22 static int processed_enough = 0;
23 static int gui_mode_reports = 0;
24 static int norollup = 1;
25 static int direct_rollup = 0;
27 static LLONG get_fts(struct s_write *wb)
32 #define fatal(N, ...) // N
33 #define XMLRPC_APPEND(N, ...) // N
35 int rowdata[] = {11,-1,1,2,3,4,12,13,14,15,5,6,7,8,9,10};
36 // Relationship between the first PAC byte and the row number
38 // The following enc_buffer is not used at the moment, if it does get used
39 // we need to bring it into the swrite struct. Same for "str".
40 #define INITIAL_ENC_BUFFER_CAPACITY 2048
42 unsigned char *enc_buffer=NULL; // Generic general purpose buffer
43 unsigned char str[2048]; // Another generic general purpose buffer
44 unsigned enc_buffer_used;
45 unsigned enc_buffer_capacity;
47 #define GUARANTEE(length) if (length>enc_buffer_capacity) \
48 {enc_buffer_capacity*=2; enc_buffer=(unsigned char*) realloc (enc_buffer, enc_buffer_capacity); \
49 if (enc_buffer==NULL) { fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory, bailing out\n"); } \
52 const unsigned char pac2_attribs[][3]= // Color, font, ident
54 {COL_WHITE, FONT_REGULAR, 0}, // 0x40 || 0x60
55 {COL_WHITE, FONT_UNDERLINED, 0}, // 0x41 || 0x61
56 {COL_GREEN, FONT_REGULAR, 0}, // 0x42 || 0x62
57 {COL_GREEN, FONT_UNDERLINED, 0}, // 0x43 || 0x63
58 {COL_BLUE, FONT_REGULAR, 0}, // 0x44 || 0x64
59 {COL_BLUE, FONT_UNDERLINED, 0}, // 0x45 || 0x65
60 {COL_CYAN, FONT_REGULAR, 0}, // 0x46 || 0x66
61 {COL_CYAN, FONT_UNDERLINED, 0}, // 0x47 || 0x67
62 {COL_RED, FONT_REGULAR, 0}, // 0x48 || 0x68
63 {COL_RED, FONT_UNDERLINED, 0}, // 0x49 || 0x69
64 {COL_YELLOW, FONT_REGULAR, 0}, // 0x4a || 0x6a
65 {COL_YELLOW, FONT_UNDERLINED, 0}, // 0x4b || 0x6b
66 {COL_MAGENTA, FONT_REGULAR, 0}, // 0x4c || 0x6c
67 {COL_MAGENTA, FONT_UNDERLINED, 0}, // 0x4d || 0x6d
68 {COL_WHITE, FONT_ITALICS, 0}, // 0x4e || 0x6e
69 {COL_WHITE, FONT_UNDERLINED_ITALICS, 0}, // 0x4f || 0x6f
70 {COL_WHITE, FONT_REGULAR, 0}, // 0x50 || 0x70
71 {COL_WHITE, FONT_UNDERLINED, 0}, // 0x51 || 0x71
72 {COL_WHITE, FONT_REGULAR, 4}, // 0x52 || 0x72
73 {COL_WHITE, FONT_UNDERLINED, 4}, // 0x53 || 0x73
74 {COL_WHITE, FONT_REGULAR, 8}, // 0x54 || 0x74
75 {COL_WHITE, FONT_UNDERLINED, 8}, // 0x55 || 0x75
76 {COL_WHITE, FONT_REGULAR, 12}, // 0x56 || 0x76
77 {COL_WHITE, FONT_UNDERLINED, 12}, // 0x57 || 0x77
78 {COL_WHITE, FONT_REGULAR, 16}, // 0x58 || 0x78
79 {COL_WHITE, FONT_UNDERLINED, 16}, // 0x59 || 0x79
80 {COL_WHITE, FONT_REGULAR, 20}, // 0x5a || 0x7a
81 {COL_WHITE, FONT_UNDERLINED, 20}, // 0x5b || 0x7b
82 {COL_WHITE, FONT_REGULAR, 24}, // 0x5c || 0x7c
83 {COL_WHITE, FONT_UNDERLINED, 24}, // 0x5d || 0x7d
84 {COL_WHITE, FONT_REGULAR, 28}, // 0x5e || 0x7e
85 {COL_WHITE, FONT_UNDERLINED, 28} // 0x5f || 0x7f
89 unsigned char encoded_crlf[16];
90 unsigned int encoded_crlf_length;
91 unsigned char encoded_br[16];
92 unsigned int encoded_br_length;
95 unsigned char usercolor_rgb[8]="";
96 enum color_code default_color=COL_WHITE;
98 const char *sami_header= // TODO: Revise the <!-- comments
101 <STYLE TYPE=\"text/css\">\n\
103 P {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;\n\
104 text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}\n\
105 .UNKNOWNCC {Name:Unknown; lang:en-US; SAMIType:CC;}\n\
111 const char *command_type[] =
114 "EDM - EraseDisplayedMemory",
115 "RCL - ResumeCaptionLoading",
116 "EOC - End Of Caption",
117 "TO1 - Tab Offset, 1 column",
118 "TO2 - Tab Offset, 2 column",
119 "TO3 - Tab Offset, 3 column",
120 "RU2 - Roll up 2 rows",
121 "RU3 - Roll up 3 rows",
122 "RU4 - Roll up 4 rows",
123 "CR - Carriage Return",
124 "ENM - Erase non-displayed memory",
126 "RTD - Resume Text Display"
129 const char *font_text[]=
137 const char *cc_modes_text[]=
142 const char *color_text[][2]=
145 {"green","<font color=\"#00ff00\">"},
146 {"blue","<font color=\"#0000ff\">"},
147 {"cyan","<font color=\"#00ffff\">"},
148 {"red","<font color=\"#ff0000\">"},
149 {"yellow","<font color=\"#ffff00\">"},
150 {"magenta","<font color=\"#ff00ff\">"},
151 {"userdefined","<font color=\""}
154 int general_608_init (struct s_write *wb)
157 * Not currently used.
161 enc_buffer=(unsigned char *) malloc (INITIAL_ENC_BUFFER_CAPACITY);
162 if (enc_buffer==NULL)
164 enc_buffer_capacity=INITIAL_ENC_BUFFER_CAPACITY;
169 wb->subline = malloc(2048);
177 wb->new_sentence = 1;
184 * Free up CC memory - don't call this from HB just yet since it will cause
185 * parallel encodes to fail - to be honest they will be stuffed anyway since
186 * the CC's may be overwriting the buffers.
188 void general_608_close (struct s_write *wb)
192 enc_buffer_capacity = 0;
203 void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
205 unsigned char c1='?';
208 // Regular line-21 character set, mostly ASCII except these exceptions
211 case 0x2a: // lowercase a, acute accent
214 case 0x5c: // lowercase e, acute accent
217 case 0x5e: // lowercase i, acute accent
220 case 0x5f: // lowercase o, acute accent
223 case 0x60: // lowercase u, acute accent
226 case 0x7b: // lowercase c with cedilla
229 case 0x7c: // division symbol
232 case 0x7d: // uppercase N tilde
235 case 0x7e: // lowercase n tilde
247 // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
248 // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
249 case 0x80: // Registered symbol (R)
252 case 0x81: // degree sign
255 case 0x82: // 1/2 symbol
258 case 0x83: // Inverted (open) question mark
261 case 0x84: // Trademark symbol (TM) - Does not exist in Latin 1
263 case 0x85: // Cents symbol
266 case 0x86: // Pounds sterling
269 case 0x87: // Music note - Not in latin 1, so we use 'pilcrow'
272 case 0x88: // lowercase a, grave accent
275 case 0x89: // transparent space, we make it regular
278 case 0x8a: // lowercase e, grave accent
281 case 0x8b: // lowercase a, circumflex accent
284 case 0x8c: // lowercase e, circumflex accent
287 case 0x8d: // lowercase i, circumflex accent
290 case 0x8e: // lowercase o, circumflex accent
293 case 0x8f: // lowercase u, circumflex accent
296 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
297 // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F
298 case 0x90: // capital letter A with acute
301 case 0x91: // capital letter E with acute
304 case 0x92: // capital letter O with acute
307 case 0x93: // capital letter U with acute
310 case 0x94: // capital letter U with diaresis
313 case 0x95: // lowercase letter U with diaeresis
316 case 0x96: // apostrophe
319 case 0x97: // inverted exclamation mark
322 case 0x98: // asterisk
325 case 0x99: // apostrophe (yes, duped). See CCADI source code.
328 case 0x9a: // hyphen-minus
331 case 0x9b: // copyright sign
334 case 0x9c: // Service Mark - not available in latin 1
336 case 0x9d: // Full stop (.)
339 case 0x9e: // Quoatation mark
342 case 0x9f: // Quoatation mark
345 case 0xa0: // uppercase A, grave accent
348 case 0xa1: // uppercase A, circumflex
351 case 0xa2: // uppercase C with cedilla
354 case 0xa3: // uppercase E, grave accent
357 case 0xa4: // uppercase E, circumflex
360 case 0xa5: // capital letter E with diaresis
363 case 0xa6: // lowercase letter e with diaresis
366 case 0xa7: // uppercase I, circumflex
369 case 0xa8: // uppercase I, with diaresis
372 case 0xa9: // lowercase i, with diaresis
375 case 0xaa: // uppercase O, circumflex
378 case 0xab: // uppercase U, grave accent
381 case 0xac: // lowercase u, grave accent
384 case 0xad: // uppercase U, circumflex
387 case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
390 case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
393 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
394 // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F
395 case 0xb0: // Uppercase A, tilde
398 case 0xb1: // Lowercase a, tilde
401 case 0xb2: // Uppercase I, acute accent
404 case 0xb3: // Uppercase I, grave accent
407 case 0xb4: // Lowercase i, grave accent
410 case 0xb5: // Uppercase O, grave accent
413 case 0xb6: // Lowercase o, grave accent
416 case 0xb7: // Uppercase O, tilde
419 case 0xb8: // Lowercase o, tilde
422 case 0xb9: // Open curly brace
425 case 0xba: // Closing curly brace
428 case 0xbb: // Backslash
434 case 0xbd: // Underscore
437 case 0xbe: // Pipe (broken bar)
443 case 0xc0: // Uppercase A, umlaut
446 case 0xc1: // Lowercase A, umlaut
449 case 0xc2: // Uppercase O, umlaut
452 case 0xc3: // Lowercase o, umlaut
455 case 0xc4: // Esszett (sharp S)
458 case 0xc5: // Yen symbol
461 case 0xc6: // Currency symbol
464 case 0xc7: // Vertical bar
467 case 0xc8: // Uppercase A, ring
470 case 0xc9: // Lowercase A, ring
473 case 0xca: // Uppercase O, slash
476 case 0xcb: // Lowercase o, slash
479 case 0xcc: // Upper left corner
480 case 0xcd: // Upper right corner
481 case 0xce: // Lower left corner
482 case 0xcf: // Lower right corner
483 default: // For those that don't have representation
484 *buffer='?'; // I'll do it eventually, I promise
485 break; // This are weird chars anyway
490 void get_char_in_unicode (unsigned char *buffer, unsigned char c)
495 case 0x84: // Trademark symbol (TM)
499 case 0x87: // Music note
503 case 0x9c: // Service Mark
507 case 0xcc: // Upper left corner
511 case 0xcd: // Upper right corner
515 case 0xce: // Lower left corner
519 case 0xcf: // Lower right corner
523 default: // Everything else, same as latin-1 followed by 00
524 get_char_in_latin_1 (&c1,c);
532 int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number of bytes used
534 if (c<0x80) // Regular line-21 character set, mostly ASCII except these exceptions
538 case 0x2a: // lowercase a, acute accent
542 case 0x5c: // lowercase e, acute accent
546 case 0x5e: // lowercase i, acute accent
550 case 0x5f: // lowercase o, acute accent
554 case 0x60: // lowercase u, acute accent
558 case 0x7b: // lowercase c with cedilla
562 case 0x7c: // division symbol
566 case 0x7d: // uppercase N tilde
570 case 0x7e: // lowercase n tilde
581 // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
582 // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
583 case 0x80: // Registered symbol (R)
587 case 0x81: // degree sign
591 case 0x82: // 1/2 symbol
595 case 0x83: // Inverted (open) question mark
599 case 0x84: // Trademark symbol (TM)
604 case 0x85: // Cents symbol
608 case 0x86: // Pounds sterling
612 case 0x87: // Music note
617 case 0x88: // lowercase a, grave accent
621 case 0x89: // transparent space, we make it regular
624 case 0x8a: // lowercase e, grave accent
628 case 0x8b: // lowercase a, circumflex accent
632 case 0x8c: // lowercase e, circumflex accent
636 case 0x8d: // lowercase i, circumflex accent
640 case 0x8e: // lowercase o, circumflex accent
644 case 0x8f: // lowercase u, circumflex accent
648 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
649 // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F
650 case 0x90: // capital letter A with acute
654 case 0x91: // capital letter E with acute
658 case 0x92: // capital letter O with acute
662 case 0x93: // capital letter U with acute
666 case 0x94: // capital letter U with diaresis
670 case 0x95: // lowercase letter U with diaeresis
674 case 0x96: // apostrophe
677 case 0x97: // inverted exclamation mark
681 case 0x98: // asterisk
684 case 0x99: // apostrophe (yes, duped). See CCADI source code.
687 case 0x9a: // hyphen-minus
690 case 0x9b: // copyright sign
694 case 0x9c: // Service mark
699 case 0x9d: // Full stop (.)
702 case 0x9e: // Quoatation mark
705 case 0x9f: // Quoatation mark
708 case 0xa0: // uppercase A, grave accent
712 case 0xa1: // uppercase A, circumflex
716 case 0xa2: // uppercase C with cedilla
720 case 0xa3: // uppercase E, grave accent
724 case 0xa4: // uppercase E, circumflex
728 case 0xa5: // capital letter E with diaresis
732 case 0xa6: // lowercase letter e with diaresis
736 case 0xa7: // uppercase I, circumflex
740 case 0xa8: // uppercase I, with diaresis
744 case 0xa9: // lowercase i, with diaresis
748 case 0xaa: // uppercase O, circumflex
752 case 0xab: // uppercase U, grave accent
756 case 0xac: // lowercase u, grave accent
760 case 0xad: // uppercase U, circumflex
764 case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
768 case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
772 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
773 // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F
774 case 0xb0: // Uppercase A, tilde
778 case 0xb1: // Lowercase a, tilde
782 case 0xb2: // Uppercase I, acute accent
786 case 0xb3: // Uppercase I, grave accent
790 case 0xb4: // Lowercase i, grave accent
794 case 0xb5: // Uppercase O, grave accent
798 case 0xb6: // Lowercase o, grave accent
802 case 0xb7: // Uppercase O, tilde
806 case 0xb8: // Lowercase o, tilde
810 case 0xb9: // Open curly brace
813 case 0xba: // Closing curly brace
816 case 0xbb: // Backslash
822 case 0xbd: // Underscore
825 case 0xbe: // Pipe (broken bar)
830 *buffer=0x7e; // Not sure
832 case 0xc0: // Uppercase A, umlaut
836 case 0xc1: // Lowercase A, umlaut
840 case 0xc2: // Uppercase O, umlaut
844 case 0xc3: // Lowercase o, umlaut
848 case 0xc4: // Esszett (sharp S)
852 case 0xc5: // Yen symbol
856 case 0xc6: // Currency symbol
860 case 0xc7: // Vertical bar
863 case 0xc8: // Uppercase A, ring
867 case 0xc9: // Lowercase A, ring
871 case 0xca: // Uppercase O, slash
875 case 0xcb: // Lowercase o, slash
879 case 0xcc: // Upper left corner
884 case 0xcd: // Upper right corner
889 case 0xce: // Lower left corner
894 case 0xcf: // Lower right corner
900 *buffer='?'; // I'll do it eventually, I promise
901 return 1; // This are weird chars anyway
905 unsigned char cctolower (unsigned char c)
907 if (c>='A' && c<='Z')
911 case 0x7d: // uppercase N tilde
913 case 0x90: // capital letter A with acute
915 case 0x91: // capital letter E with acute
917 case 0x92: // capital letter O with acute
919 case 0x93: // capital letter U with acute
921 case 0xa2: // uppercase C with cedilla
923 case 0xa0: // uppercase A, grave accent
925 case 0xa3: // uppercase E, grave accent
927 case 0xa1: // uppercase A, circumflex
929 case 0xa4: // uppercase E, circumflex
931 case 0xa7: // uppercase I, circumflex
933 case 0xaa: // uppercase O, circumflex
935 case 0xad: // uppercase U, circumflex
937 case 0x94: // capital letter U with diaresis
939 case 0xa5: // capital letter E with diaresis
941 case 0xa8: // uppercase I, with diaresis
943 case 0xab: // uppercase U, grave accent
945 case 0xb0: // Uppercase A, tilde
947 case 0xb2: // Uppercase I, acute accent
949 case 0xb3: // Uppercase I, grave accent
951 case 0xb5: // Uppercase O, grave accent
953 case 0xb7: // Uppercase O, tilde
955 case 0xc0: // Uppercase A, umlaut
957 case 0xc2: // Uppercase O, umlaut
959 case 0xc8: // Uppercase A, ring
961 case 0xca: // Uppercase O, slash
967 unsigned char cctoupper (unsigned char c)
969 if (c>='a' && c<='z')
973 case 0x7e: // lowercase n tilde
975 case 0x2a: // lowercase a, acute accent
977 case 0x5c: // lowercase e, acute accent
979 case 0x5e: // lowercase i, acute accent
981 case 0x5f: // lowercase o, acute accent
983 case 0x60: // lowercase u, acute accent
985 case 0x7b: // lowercase c with cedilla
987 case 0x88: // lowercase a, grave accent
989 case 0x8a: // lowercase e, grave accent
991 case 0x8b: // lowercase a, circumflex accent
993 case 0x8c: // lowercase e, circumflex accent
995 case 0x8d: // lowercase i, circumflex accent
997 case 0x8e: // lowercase o, circumflex accent
999 case 0x8f: // lowercase u, circumflex accent
1001 case 0x95: // lowercase letter U with diaeresis
1003 case 0xa6: // lowercase letter e with diaresis
1005 case 0xa9: // lowercase i, with diaresis
1007 case 0xac: // lowercase u, grave accent
1009 case 0xb1: // Lowercase a, tilde
1011 case 0xb4: // Lowercase i, grave accent
1013 case 0xb6: // Lowercase o, grave accent
1015 case 0xb8: // Lowercase o, tilde
1017 case 0xc1: // Lowercase A, umlaut
1019 case 0xc3: // Lowercase o, umlaut
1021 case 0xc9: // Lowercase A, ring
1023 case 0xcb: // Lowercase o, slash
1030 // Encodes a generic string. Note that since we use the encoders for closed caption
1031 // data, text would have to be encoded as CCs... so using special characters here
1033 unsigned encode_line (unsigned char *buffer, unsigned char *text)
1058 #define ISSEPARATOR(c) (c==' ' || c==0x89 || ispunct(c) \
1059 || c==0x99) // This is the apostrofe. We get it here in CC encoding, not ASCII
1062 void correct_case (int line_num, struct eia608_screen *data)
1065 /* while (i<spell_words) */
1067 /* char *c=(char *) data->characters[line_num]; */
1068 /* size_t len=strlen (spell_correct[i]); */
1069 /* while ((c=strstr (c,spell_lower[i]))!=NULL) */
1071 /* // Make sure it's a whole word (start of line or */
1072 /* // preceded by space, and end of line or followed by */
1074 /* unsigned char prev; */
1075 /* if (c==(char *) data->characters[line_num]) // Beginning of line... */
1076 /* prev=' '; // ...Pretend we had a blank before */
1079 /* unsigned char next; */
1080 /* if (c-(char *) data->characters[line_num]+len==CC608_SCREEN_WIDTH) // End of line... */
1081 /* next=' '; // ... pretend we have a blank later */
1083 /* next=*(c+len); */
1084 /* if ( ISSEPARATOR(prev) && ISSEPARATOR(next)) */
1086 /* memcpy (c,spell_correct[i],len); */
1094 void capitalize (int line_num, struct eia608_screen *data, int *new_sentence)
1098 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1100 switch (data->characters[line_num][i])
1103 case 0x89: // This is a transparent space
1106 case '.': // Fallthrough
1107 case '?': // Fallthrough
1114 data->characters[line_num][i]=cctoupper (data->characters[line_num][i]);
1116 data->characters[line_num][i]=cctolower (data->characters[line_num][i]);
1123 void find_limit_characters (unsigned char *line, int *first_non_blank, int *last_non_blank)
1128 *first_non_blank=-1;
1129 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1131 unsigned char c=line[i];
1132 if (c!=' ' && c!=0x89)
1134 if (*first_non_blank==-1)
1141 unsigned get_decoder_line_basic (unsigned char *buffer, int line_num, struct eia608_screen *data)
1143 unsigned char *line = data->characters[line_num];
1144 int last_non_blank=-1;
1145 int first_non_blank=-1;
1146 unsigned char *orig=buffer; // Keep for debugging
1148 find_limit_characters (line, &first_non_blank, &last_non_blank);
1150 if (first_non_blank==-1)
1157 for (i=first_non_blank;i<=last_non_blank;i++)
1163 bytes=get_char_in_utf_8 (buffer,c);
1166 get_char_in_latin_1 (buffer,c);
1170 get_char_in_unicode (buffer,c);
1177 return (unsigned) (buffer-orig); // Return length
1180 unsigned get_decoder_line_encoded_for_gui (unsigned char *buffer, int line_num, struct eia608_screen *data)
1182 unsigned char *line = data->characters[line_num];
1183 unsigned char *orig=buffer; // Keep for debugging
1184 int first=0, last=31;
1187 find_limit_characters(line,&first,&last);
1188 for (i=first;i<=last;i++)
1190 get_char_in_latin_1 (buffer,line[i]);
1194 return (unsigned) (buffer-orig); // Return length
1198 unsigned get_decoder_line_encoded (unsigned char *buffer, int line_num, struct eia608_screen *data)
1200 int col = COL_WHITE;
1205 unsigned char *line = data->characters[line_num];
1206 unsigned char *orig=buffer; // Keep for debugging
1207 int first=0, last=31;
1209 find_limit_characters(line,&first,&last);
1210 for (i=first;i<=last;i++)
1213 int its_col = data->colors[line_num][i];
1214 if (its_col != col && !nofontcolor)
1216 if (col!=COL_WHITE) // We need to close the previous font tag
1218 buffer+= encode_line (buffer,(unsigned char *) "</font>");
1221 buffer+=encode_line (buffer, (unsigned char*) color_text[its_col][1]);
1222 if (its_col==COL_USERDEFINED)
1224 // The previous sentence doesn't copy the whole
1225 // <font> tag, just up to the quote before the color
1226 buffer+=encode_line (buffer, (unsigned char*) usercolor_rgb);
1227 buffer+=encode_line (buffer, (unsigned char*) "\">");
1232 // Handle underlined
1233 int is_underlined = data->fonts[line_num][i] & FONT_UNDERLINED;
1234 if (is_underlined && underlined==0) // Open underline
1236 buffer+=encode_line (buffer, (unsigned char *) "<u>");
1238 if (is_underlined==0 && underlined) // Close underline
1240 buffer+=encode_line (buffer, (unsigned char *) "</u>");
1242 underlined=is_underlined;
1244 int has_ita = data->fonts[line_num][i] & FONT_ITALICS;
1245 if (has_ita && italics==0) // Open italics
1247 buffer+=encode_line (buffer, (unsigned char *) "<i>");
1249 if (has_ita==0 && italics) // Close italics
1251 buffer+=encode_line (buffer, (unsigned char *) "</i>");
1258 bytes=get_char_in_utf_8 (buffer,line[i]);
1261 get_char_in_latin_1 (buffer,line[i]);
1265 get_char_in_unicode (buffer,line[i]);
1273 buffer+=encode_line (buffer, (unsigned char *) "</i>");
1277 buffer+=encode_line (buffer, (unsigned char *) "</u>");
1279 if (col != COL_WHITE && !nofontcolor)
1281 buffer+=encode_line (buffer, (unsigned char *) "</font>");
1284 return (unsigned) (buffer-orig); // Return length
1288 void delete_all_lines_but_current (struct eia608_screen *data, int row)
1295 memset(data->characters[i],' ',CC608_SCREEN_WIDTH);
1296 data->characters[i][CC608_SCREEN_WIDTH]=0;
1297 memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1);
1298 memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1);
1299 data->row_used[i]=0;
1304 void clear_eia608_cc_buffer (struct eia608_screen *data)
1310 memset(data->characters[i],' ',CC608_SCREEN_WIDTH);
1311 data->characters[i][CC608_SCREEN_WIDTH]=0;
1312 memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1);
1313 memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1);
1314 data->row_used[i]=0;
1319 void init_eia608 (struct eia608 *data)
1321 data->cursor_column=0;
1323 clear_eia608_cc_buffer (&data->buffer1);
1324 clear_eia608_cc_buffer (&data->buffer2);
1325 data->visible_buffer=1;
1328 data->mode=MODE_POPUP;
1329 // data->current_visible_start_cc=0;
1330 data->current_visible_start_ms=0;
1331 data->srt_counter=0;
1332 data->screenfuls_counter=0;
1334 data->color=default_color;
1335 data->font=FONT_REGULAR;
1336 data->rollup_base_row=14;
1339 struct eia608_screen *get_writing_buffer (struct s_write *wb)
1341 struct eia608_screen *use_buffer=NULL;
1342 switch (wb->data608->mode)
1344 case MODE_POPUP: // Write on the non-visible buffer
1345 if (wb->data608->visible_buffer==1)
1346 use_buffer = &wb->data608->buffer2;
1348 use_buffer = &wb->data608->buffer1;
1350 case MODE_ROLLUP_2: // Write directly to screen
1353 if (wb->data608->visible_buffer==1)
1354 use_buffer = &wb->data608->buffer1;
1356 use_buffer = &wb->data608->buffer2;
1359 fatal (EXIT_BUG_BUG, "Caption mode has an illegal value at get_writing_buffer(), this is a bug.\n");
1364 void write_char (const unsigned char c, struct s_write *wb)
1366 if (wb->data608->mode!=MODE_TEXT)
1368 struct eia608_screen * use_buffer=get_writing_buffer(wb);
1369 /* hb_log ("\rWriting char [%c] at %s:%d:%d\n",c,
1370 use_buffer == &wb->data608->buffer1?"B1":"B2",
1371 wb->data608->cursor_row,wb->data608->cursor_column); */
1372 use_buffer->characters[wb->data608->cursor_row][wb->data608->cursor_column]=c;
1373 use_buffer->colors[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->color;
1374 use_buffer->fonts[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->font;
1375 use_buffer->row_used[wb->data608->cursor_row]=1;
1376 use_buffer->empty=0;
1377 if (wb->data608->cursor_column<31)
1378 wb->data608->cursor_column++;
1383 /* Handle MID-ROW CODES. */
1384 void handle_text_attr (const unsigned char c1, const unsigned char c2, struct s_write *wb)
1386 // Handle channel change
1387 wb->data608->channel=wb->new_channel;
1388 if (wb->data608->channel!=cc_channel)
1391 hb_log ("\r608: text_attr: %02X %02X",c1,c2);
1392 if ( ((c1!=0x11 && c1!=0x19) ||
1393 (c2<0x20 || c2>0x2f)) && debug_608)
1395 hb_log ("\rThis is not a text attribute!\n");
1400 wb->data608->color=pac2_attribs[i][0];
1401 wb->data608->font=pac2_attribs[i][1];
1403 hb_log (" -- Color: %s, font: %s\n",
1404 color_text[wb->data608->color][0],
1405 font_text[wb->data608->font]);
1406 if (wb->data608->cursor_column<31)
1407 wb->data608->cursor_column++;
1411 void mstotime (LLONG milli, unsigned *hours, unsigned *minutes,
1412 unsigned *seconds, unsigned *ms)
1414 // LLONG milli = (LLONG) ((ccblock*1000)/29.97);
1415 *ms=(unsigned) (milli%1000); // milliseconds
1416 milli=(milli-*ms)/1000; // Remainder, in seconds
1417 *seconds = (int) (milli%60);
1418 milli=(milli-*seconds)/60; // Remainder, in minutes
1419 *minutes = (int) (milli%60);
1420 milli=(milli-*minutes)/60; // Remainder, in hours
1424 void write_subtitle_file_footer (struct s_write *wb)
1426 switch (write_format)
1429 sprintf ((char *) str,"</BODY></SAMI>\n");
1430 if (debug_608 && encoding!=ENC_UNICODE)
1432 hb_log ("\r%s\n", str);
1434 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
1435 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1436 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1438 default: // Nothing to do. Only SAMI has a footer
1443 void fhb_log_encoded (FILE *fh, const char *string)
1445 GUARANTEE(strlen (string)*3);
1446 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) string);
1447 fwrite (enc_buffer,enc_buffer_used,1,fh);
1450 void write_subtitle_file_header (struct s_write *wb)
1452 switch (write_format)
1454 case OF_SRT: // Subrip subtitles have no header
1456 case OF_SAMI: // This header brought to you by McPoodle's CCASDI
1457 //fhb_log_encoded (wb->fh, sami_header);
1458 GUARANTEE(strlen (sami_header)*3);
1459 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) sami_header);
1460 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1461 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1463 case OF_RCWT: // Write header
1464 //fwrite (rcwt_header, sizeof(rcwt_header),1,wb->fh);
1466 case OF_TRANSCRIPT: // No header. Fall thru
1472 void write_cc_line_as_transcript (struct eia608_screen *data, struct s_write *wb, int line_number)
1474 hb_buffer_t *buffer;
1478 capitalize(line_number,data, &wb->new_sentence);
1479 correct_case(line_number,data);
1481 int length = get_decoder_line_basic (wb->subline, line_number, data);
1482 if (debug_608 && encoding!=ENC_UNICODE)
1485 hb_log ("%s\n",wb->subline);
1489 //fwrite (wb->subline, 1, length, wb->fh);
1491 * Put this subtitle in a hb_buffer_t and shove it into the subtitle fifo
1493 buffer = hb_buffer_init( length + 1 );
1494 buffer->start = wb->data608->current_visible_start_ms;
1495 buffer->stop = get_fts(wb);
1496 memcpy( buffer->data, wb->subline, length + 1 );
1497 //hb_log("CC %lld: %s", buffer->stop, wb->subline);
1499 hb_fifo_push( wb->subtitle->fifo_raw, buffer );
1501 XMLRPC_APPEND(wb->subline,length);
1502 //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1503 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1505 // fhb_log (wb->fh,encoded_crlf);
1508 int write_cc_buffer_as_transcript (struct eia608_screen *data, struct s_write *wb)
1512 int wrote_something = 0;
1515 hb_log ("\n- - - TRANSCRIPT caption - - -\n");
1519 if (data->row_used[i])
1521 write_cc_line_as_transcript (data,wb, i);
1527 hb_log ("- - - - - - - - - - - -\r\n");
1529 return wrote_something;
1532 void write_cc_buffer_to_gui (struct eia608_screen *data, struct s_write *wb)
1534 unsigned h1,m1,s1,ms1;
1535 unsigned h2,m2,s2,ms2;
1538 LLONG ms_start= wb->data608->current_visible_start_ms;
1540 ms_start+=subs_delay;
1541 if (ms_start<0) // Drop screens that because of subs_delay start too early
1543 int time_reported=0;
1546 if (data->row_used[i])
1548 hb_log ("###SUBTITLE#");
1551 LLONG ms_end = get_fts(wb)+subs_delay;
1552 mstotime (ms_start,&h1,&m1,&s1,&ms1);
1553 mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line.
1554 // Note, only MM:SS here as we need to save space in the preview window
1555 hb_log ("%02u:%02u#%02u:%02u#",
1556 h1*60+m1,s1, h2*60+m2,s2);
1562 // We don't capitalize here because whatever function that was used
1563 // before to write to file already took care of it.
1564 int length = get_decoder_line_encoded_for_gui (wb->subline, i, data);
1565 fwrite (wb->subline, 1, length, stderr);
1566 fwrite ("\n",1,1,stderr);
1572 int write_cc_buffer_as_srt (struct eia608_screen *data, struct s_write *wb)
1574 unsigned h1,m1,s1,ms1;
1575 unsigned h2,m2,s2,ms2;
1576 int wrote_something = 0;
1577 LLONG ms_start= wb->data608->current_visible_start_ms;
1580 ms_start+=subs_delay;
1581 if (ms_start<0) // Drop screens that because of subs_delay start too early
1584 LLONG ms_end = get_fts(wb)+subs_delay;
1585 mstotime (ms_start,&h1,&m1,&s1,&ms1);
1586 mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line.
1588 wb->data608->srt_counter++;
1589 sprintf (timeline,"%u\r\n",wb->data608->srt_counter);
1590 //enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline);
1591 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1592 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1593 //sprintf (timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u\r\n",
1594 // h1,m1,s1,ms1, h2,m2,s2,ms2);
1595 //enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline);
1598 hb_log ("\n- - - SRT caption - - -\n");
1601 //fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1602 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1605 if (data->row_used[i])
1609 capitalize(i,data, &wb->new_sentence);
1610 correct_case(i,data);
1612 int length = get_decoder_line_encoded (wb->subline, i, data);
1613 if (debug_608 && encoding!=ENC_UNICODE)
1616 hb_log ("%s\n",wb->subline);
1620 hb_buffer_t *buffer = hb_buffer_init( length + 1 );
1621 buffer->start = ms_start;
1622 buffer->stop = ms_end;
1623 memcpy( buffer->data, wb->subline, length + 1 );
1625 hb_fifo_push( wb->subtitle->fifo_raw, buffer );
1627 //fwrite (wb->subline, 1, length, wb->fh);
1628 XMLRPC_APPEND(wb->subline,length);
1629 //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1630 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1632 // fhb_log (wb->fh,encoded_crlf);
1638 hb_log ("- - - - - - - - - - - -\r\n");
1640 // fhb_log (wb->fh, encoded_crlf);
1641 //fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1642 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1643 return wrote_something;
1646 int write_cc_buffer_as_sami (struct eia608_screen *data, struct s_write *wb)
1648 int wrote_something=0;
1649 LLONG startms = wb->data608->current_visible_start_ms;
1652 startms+=subs_delay;
1653 if (startms<0) // Drop screens that because of subs_delay start too early
1656 LLONG endms = get_fts(wb)+subs_delay;
1657 endms--; // To prevent overlapping with next line.
1658 sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\">\r\n",startms);
1659 if (debug_608 && encoding!=ENC_UNICODE)
1661 hb_log ("\r%s\n", str);
1663 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
1664 fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1665 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1668 if (data->row_used[i])
1670 int length = get_decoder_line_encoded (wb->subline, i, data);
1671 if (debug_608 && encoding!=ENC_UNICODE)
1674 hb_log ("%s\n",wb->subline);
1676 fwrite (wb->subline, 1, length, wb->fh);
1677 XMLRPC_APPEND(wb->subline,length);
1681 fwrite (encoded_br, 1, encoded_br_length,wb->fh);
1682 XMLRPC_APPEND(encoded_br, encoded_br_length);
1684 fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1685 XMLRPC_APPEND(encoded_crlf, encoded_crlf_length);
1688 sprintf ((char *) str,"</P></SYNC>\r\n");
1689 if (debug_608 && encoding!=ENC_UNICODE)
1691 hb_log ("\r%s\n", str);
1693 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
1694 fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1695 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1696 sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",endms);
1697 if (debug_608 && encoding!=ENC_UNICODE)
1699 hb_log ("\r%s\n", str);
1701 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
1702 fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1703 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1704 return wrote_something;
1707 struct eia608_screen *get_current_visible_buffer (struct s_write *wb)
1709 struct eia608_screen *data;
1710 if (wb->data608->visible_buffer==1)
1711 data = &wb->data608->buffer1;
1713 data = &wb->data608->buffer2;
1718 int write_cc_buffer (struct s_write *wb)
1720 struct eia608_screen *data;
1721 int wrote_something=0;
1722 if (screens_to_process!=-1 && wb->data608->screenfuls_counter>=screens_to_process)
1728 if (wb->data608->visible_buffer==1)
1729 data = &wb->data608->buffer1;
1731 data = &wb->data608->buffer2;
1736 switch (write_format)
1739 wrote_something = write_cc_buffer_as_srt (data, wb);
1742 wrote_something = write_cc_buffer_as_sami (data,wb);
1745 wrote_something = write_cc_buffer_as_transcript (data,wb);
1750 if (wrote_something && gui_mode_reports)
1751 write_cc_buffer_to_gui (data,wb);
1753 return wrote_something;
1756 void roll_up(struct s_write *wb)
1758 struct eia608_screen *use_buffer;
1761 if (wb->data608->visible_buffer==1)
1762 use_buffer = &wb->data608->buffer1;
1764 use_buffer = &wb->data608->buffer2;
1766 switch (wb->data608->mode)
1777 default: // Shouldn't happen
1781 int firstrow=-1, lastrow=-1;
1782 // Look for the last line used
1783 int rows_now=0; // Number of rows in use right now
1786 if (use_buffer->row_used[i])
1796 hb_log ("\rIn roll-up: %d lines used, first: %d, last: %d\n", rows_now, firstrow, lastrow);
1798 if (lastrow==-1) // Empty screen, nothing to rollup
1801 for (j=lastrow-keep_lines+1;j<lastrow; j++)
1805 memcpy (use_buffer->characters[j],use_buffer->characters[j+1],CC608_SCREEN_WIDTH+1);
1806 memcpy (use_buffer->colors[j],use_buffer->colors[j+1],CC608_SCREEN_WIDTH+1);
1807 memcpy (use_buffer->fonts[j],use_buffer->fonts[j+1],CC608_SCREEN_WIDTH+1);
1808 use_buffer->row_used[j]=use_buffer->row_used[j+1];
1811 for (j=0;j<(1+wb->data608->cursor_row-keep_lines);j++)
1813 memset(use_buffer->characters[j],' ',CC608_SCREEN_WIDTH);
1814 memset(use_buffer->colors[j],COL_WHITE,CC608_SCREEN_WIDTH);
1815 memset(use_buffer->fonts[j],FONT_REGULAR,CC608_SCREEN_WIDTH);
1816 use_buffer->characters[j][CC608_SCREEN_WIDTH]=0;
1817 use_buffer->row_used[j]=0;
1819 memset(use_buffer->characters[lastrow],' ',CC608_SCREEN_WIDTH);
1820 memset(use_buffer->colors[lastrow],COL_WHITE,CC608_SCREEN_WIDTH);
1821 memset(use_buffer->fonts[lastrow],FONT_REGULAR,CC608_SCREEN_WIDTH);
1823 use_buffer->characters[lastrow][CC608_SCREEN_WIDTH]=0;
1824 use_buffer->row_used[lastrow]=0;
1829 if (use_buffer->row_used[i])
1831 if (rows_now>keep_lines)
1832 hb_log ("Bug in roll_up, should have %d lines but I have %d.\n",
1833 keep_lines, rows_now);
1836 void erase_memory (struct s_write *wb, int displayed)
1838 struct eia608_screen *buf;
1841 if (wb->data608->visible_buffer==1)
1842 buf=&wb->data608->buffer1;
1844 buf=&wb->data608->buffer2;
1848 if (wb->data608->visible_buffer==1)
1849 buf=&wb->data608->buffer2;
1851 buf=&wb->data608->buffer1;
1853 clear_eia608_cc_buffer (buf);
1856 int is_current_row_empty (struct s_write *wb)
1858 struct eia608_screen *use_buffer;
1861 if (wb->data608->visible_buffer==1)
1862 use_buffer = &wb->data608->buffer1;
1864 use_buffer = &wb->data608->buffer2;
1865 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1867 if (use_buffer->characters[wb->data608->rollup_base_row][i]!=' ')
1873 /* Process GLOBAL CODES */
1874 void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct s_write *wb)
1876 // Handle channel change
1877 wb->data608->channel=wb->new_channel;
1878 if (wb->data608->channel!=cc_channel)
1881 enum command_code command = COM_UNKNOWN;
1884 if ((c1==0x14 || c1==0x1C) && c2==0x2C)
1885 command = COM_ERASEDISPLAYEDMEMORY;
1886 if ((c1==0x14 || c1==0x1C) && c2==0x20)
1887 command = COM_RESUMECAPTIONLOADING;
1888 if ((c1==0x14 || c1==0x1C) && c2==0x2F)
1889 command = COM_ENDOFCAPTION;
1890 if ((c1==0x17 || c1==0x1F) && c2==0x21)
1891 command = COM_TABOFFSET1;
1892 if ((c1==0x17 || c1==0x1F) && c2==0x22)
1893 command = COM_TABOFFSET2;
1894 if ((c1==0x17 || c1==0x1F) && c2==0x23)
1895 command = COM_TABOFFSET3;
1896 if ((c1==0x14 || c1==0x1C) && c2==0x25)
1897 command = COM_ROLLUP2;
1898 if ((c1==0x14 || c1==0x1C) && c2==0x26)
1899 command = COM_ROLLUP3;
1900 if ((c1==0x14 || c1==0x1C) && c2==0x27)
1901 command = COM_ROLLUP4;
1902 if ((c1==0x14 || c1==0x1C) && c2==0x2D)
1903 command = COM_CARRIAGERETURN;
1904 if ((c1==0x14 || c1==0x1C) && c2==0x2E)
1905 command = COM_ERASENONDISPLAYEDMEMORY;
1906 if ((c1==0x14 || c1==0x1C) && c2==0x21)
1907 command = COM_BACKSPACE;
1908 if ((c1==0x14 || c1==0x1C) && c2==0x2b)
1909 command = COM_RESUMETEXTDISPLAY;
1912 hb_log ("\rCommand: %02X %02X (%s)\n",c1,c2,command_type[command]);
1917 if (wb->data608->cursor_column>0)
1919 wb->data608->cursor_column--;
1920 get_writing_buffer(wb)->characters[wb->data608->cursor_row][wb->data608->cursor_column]=' ';
1923 case COM_TABOFFSET1:
1924 if (wb->data608->cursor_column<31)
1925 wb->data608->cursor_column++;
1927 case COM_TABOFFSET2:
1928 wb->data608->cursor_column+=2;
1929 if (wb->data608->cursor_column>31)
1930 wb->data608->cursor_column=31;
1932 case COM_TABOFFSET3:
1933 wb->data608->cursor_column+=3;
1934 if (wb->data608->cursor_column>31)
1935 wb->data608->cursor_column=31;
1937 case COM_RESUMECAPTIONLOADING:
1938 wb->data608->mode=MODE_POPUP;
1940 case COM_RESUMETEXTDISPLAY:
1941 wb->data608->mode=MODE_TEXT;
1944 if (wb->data608->mode==MODE_POPUP)
1946 if (write_cc_buffer (wb))
1947 wb->data608->screenfuls_counter++;
1948 erase_memory (wb, 1);
1950 if (wb->data608->mode==MODE_ROLLUP_2 && !is_current_row_empty(wb))
1953 hb_log ("Two RU2, current line not empty. Simulating a CR\n");
1954 handle_command(0x14, 0x2D, wb);
1956 wb->data608->mode=MODE_ROLLUP_2;
1957 erase_memory (wb, 0);
1958 wb->data608->cursor_column=0;
1959 wb->data608->cursor_row=wb->data608->rollup_base_row;
1962 if (wb->data608->mode==MODE_POPUP)
1964 if (write_cc_buffer (wb))
1965 wb->data608->screenfuls_counter++;
1966 erase_memory (wb, 1);
1968 if (wb->data608->mode==MODE_ROLLUP_3 && !is_current_row_empty(wb))
1971 hb_log ("Two RU3, current line not empty. Simulating a CR\n");
1972 handle_command(0x14, 0x2D, wb);
1974 wb->data608->mode=MODE_ROLLUP_3;
1975 erase_memory (wb, 0);
1976 wb->data608->cursor_column=0;
1977 wb->data608->cursor_row=wb->data608->rollup_base_row;
1980 if (wb->data608->mode==MODE_POPUP)
1982 if (write_cc_buffer (wb))
1983 wb->data608->screenfuls_counter++;
1984 erase_memory (wb, 1);
1986 if (wb->data608->mode==MODE_ROLLUP_4 && !is_current_row_empty(wb))
1989 hb_log ("Two RU4, current line not empty. Simulating a CR\n");
1990 handle_command(0x14, 0x2D, wb);
1993 wb->data608->mode=MODE_ROLLUP_4;
1994 wb->data608->cursor_column=0;
1995 wb->data608->cursor_row=wb->data608->rollup_base_row;
1996 erase_memory (wb, 0);
1998 case COM_CARRIAGERETURN:
1999 // In transcript mode, CR doesn't write the whole screen, to avoid
2001 if (write_format==OF_TRANSCRIPT)
2003 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
2008 delete_all_lines_but_current (get_current_visible_buffer (wb), wb->data608->cursor_row);
2009 if (write_cc_buffer(wb))
2010 wb->data608->screenfuls_counter++;
2013 wb->data608->current_visible_start_ms=get_fts(wb);
2014 wb->data608->cursor_column=0;
2016 case COM_ERASENONDISPLAYEDMEMORY:
2017 erase_memory (wb,0);
2019 case COM_ERASEDISPLAYEDMEMORY:
2020 // Write it to disk before doing this, and make a note of the new
2021 // time it became clear.
2022 if (write_format==OF_TRANSCRIPT &&
2023 (wb->data608->mode==MODE_ROLLUP_2 || wb->data608->mode==MODE_ROLLUP_3 ||
2024 wb->data608->mode==MODE_ROLLUP_4))
2026 // In transcript mode we just write the cursor line. The previous lines
2027 // should have been written already, so writing everything produces
2029 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
2033 if (write_cc_buffer (wb))
2034 wb->data608->screenfuls_counter++;
2036 erase_memory (wb,1);
2037 wb->data608->current_visible_start_ms=get_fts(wb);
2039 case COM_ENDOFCAPTION: // Switch buffers
2040 // The currently *visible* buffer is leaving, so now we know it's ending
2041 // time. Time to actually write it to file.
2042 if (write_cc_buffer (wb))
2043 wb->data608->screenfuls_counter++;
2044 wb->data608->visible_buffer = (wb->data608->visible_buffer==1) ? 2 : 1;
2045 wb->data608->current_visible_start_ms=get_fts(wb);
2046 wb->data608->cursor_column=0;
2047 wb->data608->cursor_row=0;
2048 wb->data608->color=default_color;
2049 wb->data608->font=FONT_REGULAR;
2054 hb_log ("\rNot yet implemented.\n");
2060 void handle_end_of_data (struct s_write *wb)
2062 hb_buffer_t *buffer;
2064 // We issue a EraseDisplayedMemory here so if there's any captions pending
2065 // they get written to file.
2066 handle_command (0x14, 0x2c, wb); // EDM
2069 * At the end of the subtitle stream HB wants an empty buffer
2071 buffer = hb_buffer_init( 0 );
2072 hb_fifo_push( wb->subtitle->fifo_raw, buffer );
2075 void handle_double (const unsigned char c1, const unsigned char c2, struct s_write *wb)
2078 if (wb->data608->channel!=cc_channel)
2080 if (c2>=0x30 && c2<=0x3f)
2082 c=c2 + 0x50; // So if c>=0x80 && c<=0x8f, it comes from here
2084 hb_log ("\rDouble: %02X %02X --> %c\n",c1,c2,c);
2089 /* Process EXTENDED CHARACTERS */
2090 unsigned char handle_extended (unsigned char hi, unsigned char lo, struct s_write *wb)
2092 // Handle channel change
2093 if (wb->new_channel > 2)
2095 wb->new_channel -= 2;
2097 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2099 wb->data608->channel=wb->new_channel;
2100 if (wb->data608->channel!=cc_channel)
2103 // For lo values between 0x20-0x3f
2107 hb_log ("\rExtended: %02X %02X\n",hi,lo);
2108 if (lo>=0x20 && lo<=0x3f && (hi==0x12 || hi==0x13))
2113 c=lo+0x70; // So if c>=0x90 && c<=0xaf it comes from here
2116 c=lo+0x90; // So if c>=0xb0 && c<=0xcf it comes from here
2119 // This column change is because extended characters replace
2120 // the previous character (which is sent for basic decoders
2121 // to show something similar to the real char)
2122 if (wb->data608->cursor_column>0)
2123 wb->data608->cursor_column--;
2130 /* Process PREAMBLE ACCESS CODES (PAC) */
2131 void handle_pac (unsigned char c1, unsigned char c2, struct s_write *wb)
2133 // Handle channel change
2134 if (wb->new_channel > 2)
2136 wb->new_channel -= 2;
2138 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2140 wb->data608->channel=wb->new_channel;
2141 if (wb->data608->channel!=cc_channel)
2144 int row=rowdata[((c1<<1)&14)|((c2>>5)&1)];
2147 hb_log ("\rPAC: %02X %02X",c1,c2);
2149 if (c2>=0x40 && c2<=0x5f)
2155 if (c2>=0x60 && c2<=0x7f)
2162 hb_log ("\rThis is not a PAC!!!!!\n");
2166 int color=pac2_attribs[c2][0];
2167 int font=pac2_attribs[c2][1];
2168 int indent=pac2_attribs[c2][2];
2170 hb_log (" -- Position: %d:%d, color: %s, font: %s\n",row,
2171 indent,color_text[color][0],font_text[font]);
2172 if (wb->data608->mode!=MODE_TEXT)
2174 // According to Robson, row info is discarded in text mode
2175 // but column is accepted
2176 wb->data608->cursor_row=row-1 ; // Since the array is 0 based
2178 wb->data608->rollup_base_row=row-1;
2179 wb->data608->cursor_column=indent;
2183 void handle_single (const unsigned char c1, struct s_write *wb)
2185 if (c1<0x20 || wb->data608->channel!=cc_channel)
2186 return; // We don't allow special stuff here
2191 hb_log ("Character: %02X (%c) -> %02X (%c)\n",c1,c1,c,c); */
2195 int check_channel (unsigned char c1, struct s_write *wb)
2199 if (debug_608 && wb->data608->channel!=1)
2200 hb_log ("\nChannel change, now 1\n");
2205 if (debug_608 && wb->data608->channel!=2)
2206 hb_log ("\nChannel change, now 2\n");
2211 if (debug_608 && wb->data608->channel!=3)
2212 hb_log ("\nChannel change, now 3\n");
2217 if (debug_608 && wb->data608->channel!=4)
2218 hb_log ("\nChannel change, now 4\n");
2222 // Otherwise keep the current channel
2223 return wb->data608->channel;
2226 /* Handle Command, special char or attribute and also check for
2228 * Returns 1 if something was written to screen, 0 otherwise */
2229 int disCommand (unsigned char hi, unsigned char lo, struct s_write *wb)
2231 int wrote_to_screen=0;
2233 /* Full channel changes are only allowed for "GLOBAL CODES",
2234 * "OTHER POSITIONING CODES", "BACKGROUND COLOR CODES",
2236 * "PREAMBLE ACCESS CODES", "BACKGROUND COLOR CODES" and
2237 * SPECIAL/SPECIAL CHARACTERS allow only switching
2238 * between 1&3 or 2&4. */
2239 wb->new_channel = check_channel (hi,wb);
2240 //if (wb->data608->channel!=cc_channel)
2243 if (hi>=0x18 && hi<=0x1f)
2249 if (lo>=0x40 && lo<=0x5f)
2250 handle_pac (hi,lo,wb);
2253 if (lo>=0x20 && lo<=0x2f)
2254 handle_text_attr (hi,lo,wb);
2255 if (lo>=0x30 && lo<=0x3f)
2258 handle_double (hi,lo,wb);
2260 if (lo>=0x40 && lo<=0x7f)
2261 handle_pac (hi,lo,wb);
2265 if (lo>=0x20 && lo<=0x3f)
2267 wrote_to_screen=handle_extended (hi,lo,wb);
2269 if (lo>=0x40 && lo<=0x7f)
2270 handle_pac (hi,lo,wb);
2274 if (lo>=0x20 && lo<=0x2f)
2275 handle_command (hi,lo,wb);
2276 if (lo>=0x40 && lo<=0x7f)
2277 handle_pac (hi,lo,wb);
2280 if (lo>=0x40 && lo<=0x7f)
2281 handle_pac (hi,lo,wb);
2284 if (lo>=0x21 && lo<=0x22)
2285 handle_command (hi,lo,wb);
2286 if (lo>=0x2e && lo<=0x2f)
2287 handle_text_attr (hi,lo,wb);
2288 if (lo>=0x40 && lo<=0x7f)
2289 handle_pac (hi,lo,wb);
2292 return wrote_to_screen;
2295 void process608 (const unsigned char *data, int length, struct s_write *wb)
2297 static int textprinted = 0;
2302 for (i=0;i<length;i=i+2)
2304 unsigned char hi, lo;
2305 int wrote_to_screen=0;
2306 hi = data[i] & 0x7F; // Get rid of parity bit
2307 lo = data[i+1] & 0x7F; // Get rid of parity bit
2309 if (hi==0 && lo==0) // Just padding
2311 // hb_log ("\r[%02X:%02X]\n",hi,lo);
2313 if (hi>=0x01 && hi<=0x0E)
2315 // XDS crap - mode. Would be nice to support it eventually
2316 // wb->data608->last_c1=0;
2317 // wb->data608->last_c2=0;
2318 wb->data608->channel=3; // Always channel 3
2321 if (hi==0x0F) // End of XDS block
2326 if (hi>=0x10 && hi<0x1F) // Non-character code or special/extended char
2327 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML
2328 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML
2330 // We were writing characters before, start a new line for
2331 // diagnostic output from disCommand()
2332 if (debug_608 && textprinted == 1 )
2338 wb->in_xds_mode=0; // Back to normal
2339 if (wb->data608->last_c1==hi && wb->data608->last_c2==lo)
2341 // Duplicate dual code, discard
2344 wb->data608->last_c1=hi;
2345 wb->data608->last_c2=lo;
2346 wrote_to_screen=disCommand (hi,lo,wb);
2348 if (hi>=0x20) // Standard characters (always in pairs)
2350 // Only print if the channel is active
2351 if (wb->data608->channel!=cc_channel)
2356 if( textprinted == 0 )
2363 handle_single(hi,wb);
2364 handle_single(lo,wb);
2366 wb->data608->last_c1=0;
2367 wb->data608->last_c2=0;
2370 if ( debug_608 && !textprinted && wb->data608->channel==cc_channel )
2371 { // Current FTS information after the characters are shown
2372 //hb_log("Current FTS: %s\n", print_mstime(get_fts()));
2375 if (wrote_to_screen && direct_rollup && // If direct_rollup is enabled and
2376 (wb->data608->mode==MODE_ROLLUP_2 || // we are in rollup mode, write now.
2377 wb->data608->mode==MODE_ROLLUP_3 ||
2378 wb->data608->mode==MODE_ROLLUP_4))
2380 // We don't increase screenfuls_counter here.
2381 write_cc_buffer (wb);
2382 wb->data608->current_visible_start_ms=get_fts(wb);
2389 /* Return a pointer to a string that holds the printable characters
2390 * of the caption data block. FOR DEBUG PURPOSES ONLY! */
2391 unsigned char *debug_608toASC (unsigned char *cc_data, int channel)
2393 static unsigned char output[3];
2395 unsigned char cc_valid = (cc_data[0] & 4) >>2;
2396 unsigned char cc_type = cc_data[0] & 3;
2397 unsigned char hi, lo;
2403 if (cc_valid && cc_type==channel)
2405 hi = cc_data[1] & 0x7F; // Get rid of parity bit
2406 lo = cc_data[2] & 0x7F; // Get rid of parity bit
2410 output[1]=(lo>=20 ? lo : '.');