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_TRANSCRIPT;
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(void)
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( strlen( wb->subline ) + 1 );
1494 buffer->start = wb->last_pts;
1495 buffer->stop = wb->last_pts+1;
1496 strcpy( buffer->data, wb->subline );
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()+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()+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);
1618 fwrite (wb->subline, 1, length, wb->fh);
1619 XMLRPC_APPEND(wb->subline,length);
1620 fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1621 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1623 // fhb_log (wb->fh,encoded_crlf);
1628 hb_log ("- - - - - - - - - - - -\r\n");
1630 // fhb_log (wb->fh, encoded_crlf);
1631 fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1632 XMLRPC_APPEND(encoded_crlf,encoded_crlf_length);
1633 return wrote_something;
1636 int write_cc_buffer_as_sami (struct eia608_screen *data, struct s_write *wb)
1638 int wrote_something=0;
1639 LLONG startms = wb->data608->current_visible_start_ms;
1642 startms+=subs_delay;
1643 if (startms<0) // Drop screens that because of subs_delay start too early
1646 LLONG endms = get_fts()+subs_delay;
1647 endms--; // To prevent overlapping with next line.
1648 sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\">\r\n",startms);
1649 if (debug_608 && encoding!=ENC_UNICODE)
1651 hb_log ("\r%s\n", str);
1653 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
1654 fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1655 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1658 if (data->row_used[i])
1660 int length = get_decoder_line_encoded (wb->subline, i, data);
1661 if (debug_608 && encoding!=ENC_UNICODE)
1664 hb_log ("%s\n",wb->subline);
1666 fwrite (wb->subline, 1, length, wb->fh);
1667 XMLRPC_APPEND(wb->subline,length);
1671 fwrite (encoded_br, 1, encoded_br_length,wb->fh);
1672 XMLRPC_APPEND(encoded_br, encoded_br_length);
1674 fwrite (encoded_crlf, 1, encoded_crlf_length,wb->fh);
1675 XMLRPC_APPEND(encoded_crlf, encoded_crlf_length);
1678 sprintf ((char *) str,"</P></SYNC>\r\n");
1679 if (debug_608 && encoding!=ENC_UNICODE)
1681 hb_log ("\r%s\n", str);
1683 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
1684 fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1685 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1686 sprintf ((char *) str,"<SYNC start=\"%llu\"><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",endms);
1687 if (debug_608 && encoding!=ENC_UNICODE)
1689 hb_log ("\r%s\n", str);
1691 enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
1692 fwrite (enc_buffer,enc_buffer_used,1,wb->fh);
1693 XMLRPC_APPEND(enc_buffer,enc_buffer_used);
1694 return wrote_something;
1697 struct eia608_screen *get_current_visible_buffer (struct s_write *wb)
1699 struct eia608_screen *data;
1700 if (wb->data608->visible_buffer==1)
1701 data = &wb->data608->buffer1;
1703 data = &wb->data608->buffer2;
1708 int write_cc_buffer (struct s_write *wb)
1710 struct eia608_screen *data;
1711 int wrote_something=0;
1712 if (screens_to_process!=-1 && wb->data608->screenfuls_counter>=screens_to_process)
1718 if (wb->data608->visible_buffer==1)
1719 data = &wb->data608->buffer1;
1721 data = &wb->data608->buffer2;
1726 switch (write_format)
1729 wrote_something = write_cc_buffer_as_srt (data, wb);
1732 wrote_something = write_cc_buffer_as_sami (data,wb);
1735 wrote_something = write_cc_buffer_as_transcript (data,wb);
1740 if (wrote_something && gui_mode_reports)
1741 write_cc_buffer_to_gui (data,wb);
1743 return wrote_something;
1746 void roll_up(struct s_write *wb)
1748 struct eia608_screen *use_buffer;
1751 if (wb->data608->visible_buffer==1)
1752 use_buffer = &wb->data608->buffer1;
1754 use_buffer = &wb->data608->buffer2;
1756 switch (wb->data608->mode)
1767 default: // Shouldn't happen
1771 int firstrow=-1, lastrow=-1;
1772 // Look for the last line used
1773 int rows_now=0; // Number of rows in use right now
1776 if (use_buffer->row_used[i])
1786 hb_log ("\rIn roll-up: %d lines used, first: %d, last: %d\n", rows_now, firstrow, lastrow);
1788 if (lastrow==-1) // Empty screen, nothing to rollup
1791 for (j=lastrow-keep_lines+1;j<lastrow; j++)
1795 memcpy (use_buffer->characters[j],use_buffer->characters[j+1],CC608_SCREEN_WIDTH+1);
1796 memcpy (use_buffer->colors[j],use_buffer->colors[j+1],CC608_SCREEN_WIDTH+1);
1797 memcpy (use_buffer->fonts[j],use_buffer->fonts[j+1],CC608_SCREEN_WIDTH+1);
1798 use_buffer->row_used[j]=use_buffer->row_used[j+1];
1801 for (j=0;j<(1+wb->data608->cursor_row-keep_lines);j++)
1803 memset(use_buffer->characters[j],' ',CC608_SCREEN_WIDTH);
1804 memset(use_buffer->colors[j],COL_WHITE,CC608_SCREEN_WIDTH);
1805 memset(use_buffer->fonts[j],FONT_REGULAR,CC608_SCREEN_WIDTH);
1806 use_buffer->characters[j][CC608_SCREEN_WIDTH]=0;
1807 use_buffer->row_used[j]=0;
1809 memset(use_buffer->characters[lastrow],' ',CC608_SCREEN_WIDTH);
1810 memset(use_buffer->colors[lastrow],COL_WHITE,CC608_SCREEN_WIDTH);
1811 memset(use_buffer->fonts[lastrow],FONT_REGULAR,CC608_SCREEN_WIDTH);
1813 use_buffer->characters[lastrow][CC608_SCREEN_WIDTH]=0;
1814 use_buffer->row_used[lastrow]=0;
1819 if (use_buffer->row_used[i])
1821 if (rows_now>keep_lines)
1822 hb_log ("Bug in roll_up, should have %d lines but I have %d.\n",
1823 keep_lines, rows_now);
1826 void erase_memory (struct s_write *wb, int displayed)
1828 struct eia608_screen *buf;
1831 if (wb->data608->visible_buffer==1)
1832 buf=&wb->data608->buffer1;
1834 buf=&wb->data608->buffer2;
1838 if (wb->data608->visible_buffer==1)
1839 buf=&wb->data608->buffer2;
1841 buf=&wb->data608->buffer1;
1843 clear_eia608_cc_buffer (buf);
1846 int is_current_row_empty (struct s_write *wb)
1848 struct eia608_screen *use_buffer;
1851 if (wb->data608->visible_buffer==1)
1852 use_buffer = &wb->data608->buffer1;
1854 use_buffer = &wb->data608->buffer2;
1855 for (i=0;i<CC608_SCREEN_WIDTH;i++)
1857 if (use_buffer->characters[wb->data608->rollup_base_row][i]!=' ')
1863 /* Process GLOBAL CODES */
1864 void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct s_write *wb)
1866 // Handle channel change
1867 wb->data608->channel=wb->new_channel;
1868 if (wb->data608->channel!=cc_channel)
1871 enum command_code command = COM_UNKNOWN;
1874 if ((c1==0x14 || c1==0x1C) && c2==0x2C)
1875 command = COM_ERASEDISPLAYEDMEMORY;
1876 if ((c1==0x14 || c1==0x1C) && c2==0x20)
1877 command = COM_RESUMECAPTIONLOADING;
1878 if ((c1==0x14 || c1==0x1C) && c2==0x2F)
1879 command = COM_ENDOFCAPTION;
1880 if ((c1==0x17 || c1==0x1F) && c2==0x21)
1881 command = COM_TABOFFSET1;
1882 if ((c1==0x17 || c1==0x1F) && c2==0x22)
1883 command = COM_TABOFFSET2;
1884 if ((c1==0x17 || c1==0x1F) && c2==0x23)
1885 command = COM_TABOFFSET3;
1886 if ((c1==0x14 || c1==0x1C) && c2==0x25)
1887 command = COM_ROLLUP2;
1888 if ((c1==0x14 || c1==0x1C) && c2==0x26)
1889 command = COM_ROLLUP3;
1890 if ((c1==0x14 || c1==0x1C) && c2==0x27)
1891 command = COM_ROLLUP4;
1892 if ((c1==0x14 || c1==0x1C) && c2==0x2D)
1893 command = COM_CARRIAGERETURN;
1894 if ((c1==0x14 || c1==0x1C) && c2==0x2E)
1895 command = COM_ERASENONDISPLAYEDMEMORY;
1896 if ((c1==0x14 || c1==0x1C) && c2==0x21)
1897 command = COM_BACKSPACE;
1898 if ((c1==0x14 || c1==0x1C) && c2==0x2b)
1899 command = COM_RESUMETEXTDISPLAY;
1902 hb_log ("\rCommand: %02X %02X (%s)\n",c1,c2,command_type[command]);
1907 if (wb->data608->cursor_column>0)
1909 wb->data608->cursor_column--;
1910 get_writing_buffer(wb)->characters[wb->data608->cursor_row][wb->data608->cursor_column]=' ';
1913 case COM_TABOFFSET1:
1914 if (wb->data608->cursor_column<31)
1915 wb->data608->cursor_column++;
1917 case COM_TABOFFSET2:
1918 wb->data608->cursor_column+=2;
1919 if (wb->data608->cursor_column>31)
1920 wb->data608->cursor_column=31;
1922 case COM_TABOFFSET3:
1923 wb->data608->cursor_column+=3;
1924 if (wb->data608->cursor_column>31)
1925 wb->data608->cursor_column=31;
1927 case COM_RESUMECAPTIONLOADING:
1928 wb->data608->mode=MODE_POPUP;
1930 case COM_RESUMETEXTDISPLAY:
1931 wb->data608->mode=MODE_TEXT;
1934 if (wb->data608->mode==MODE_POPUP)
1936 if (write_cc_buffer (wb))
1937 wb->data608->screenfuls_counter++;
1938 erase_memory (wb, 1);
1940 if (wb->data608->mode==MODE_ROLLUP_2 && !is_current_row_empty(wb))
1943 hb_log ("Two RU2, current line not empty. Simulating a CR\n");
1944 handle_command(0x14, 0x2D, wb);
1946 wb->data608->mode=MODE_ROLLUP_2;
1947 erase_memory (wb, 0);
1948 wb->data608->cursor_column=0;
1949 wb->data608->cursor_row=wb->data608->rollup_base_row;
1952 if (wb->data608->mode==MODE_POPUP)
1954 if (write_cc_buffer (wb))
1955 wb->data608->screenfuls_counter++;
1956 erase_memory (wb, 1);
1958 if (wb->data608->mode==MODE_ROLLUP_3 && !is_current_row_empty(wb))
1961 hb_log ("Two RU3, current line not empty. Simulating a CR\n");
1962 handle_command(0x14, 0x2D, wb);
1964 wb->data608->mode=MODE_ROLLUP_3;
1965 erase_memory (wb, 0);
1966 wb->data608->cursor_column=0;
1967 wb->data608->cursor_row=wb->data608->rollup_base_row;
1970 if (wb->data608->mode==MODE_POPUP)
1972 if (write_cc_buffer (wb))
1973 wb->data608->screenfuls_counter++;
1974 erase_memory (wb, 1);
1976 if (wb->data608->mode==MODE_ROLLUP_4 && !is_current_row_empty(wb))
1979 hb_log ("Two RU4, current line not empty. Simulating a CR\n");
1980 handle_command(0x14, 0x2D, wb);
1983 wb->data608->mode=MODE_ROLLUP_4;
1984 wb->data608->cursor_column=0;
1985 wb->data608->cursor_row=wb->data608->rollup_base_row;
1986 erase_memory (wb, 0);
1988 case COM_CARRIAGERETURN:
1989 // In transcript mode, CR doesn't write the whole screen, to avoid
1991 if (write_format==OF_TRANSCRIPT)
1993 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
1998 delete_all_lines_but_current (get_current_visible_buffer (wb), wb->data608->cursor_row);
1999 if (write_cc_buffer(wb))
2000 wb->data608->screenfuls_counter++;
2003 wb->data608->current_visible_start_ms=get_fts();
2004 wb->data608->cursor_column=0;
2006 case COM_ERASENONDISPLAYEDMEMORY:
2007 erase_memory (wb,0);
2009 case COM_ERASEDISPLAYEDMEMORY:
2010 // Write it to disk before doing this, and make a note of the new
2011 // time it became clear.
2012 if (write_format==OF_TRANSCRIPT &&
2013 (wb->data608->mode==MODE_ROLLUP_2 || wb->data608->mode==MODE_ROLLUP_3 ||
2014 wb->data608->mode==MODE_ROLLUP_4))
2016 // In transcript mode we just write the cursor line. The previous lines
2017 // should have been written already, so writing everything produces
2019 write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row);
2023 if (write_cc_buffer (wb))
2024 wb->data608->screenfuls_counter++;
2026 erase_memory (wb,1);
2027 wb->data608->current_visible_start_ms=get_fts();
2029 case COM_ENDOFCAPTION: // Switch buffers
2030 // The currently *visible* buffer is leaving, so now we know it's ending
2031 // time. Time to actually write it to file.
2032 if (write_cc_buffer (wb))
2033 wb->data608->screenfuls_counter++;
2034 wb->data608->visible_buffer = (wb->data608->visible_buffer==1) ? 2 : 1;
2035 wb->data608->current_visible_start_ms=get_fts();
2036 wb->data608->cursor_column=0;
2037 wb->data608->cursor_row=0;
2038 wb->data608->color=default_color;
2039 wb->data608->font=FONT_REGULAR;
2044 hb_log ("\rNot yet implemented.\n");
2050 void handle_end_of_data (struct s_write *wb)
2052 hb_buffer_t *buffer;
2054 // We issue a EraseDisplayedMemory here so if there's any captions pending
2055 // they get written to file.
2056 handle_command (0x14, 0x2c, wb); // EDM
2059 * At the end of the subtitle stream HB wants an empty buffer
2061 buffer = hb_buffer_init( 0 );
2062 hb_fifo_push( wb->subtitle->fifo_raw, buffer );
2065 void handle_double (const unsigned char c1, const unsigned char c2, struct s_write *wb)
2068 if (wb->data608->channel!=cc_channel)
2070 if (c2>=0x30 && c2<=0x3f)
2072 c=c2 + 0x50; // So if c>=0x80 && c<=0x8f, it comes from here
2074 hb_log ("\rDouble: %02X %02X --> %c\n",c1,c2,c);
2079 /* Process EXTENDED CHARACTERS */
2080 unsigned char handle_extended (unsigned char hi, unsigned char lo, struct s_write *wb)
2082 // Handle channel change
2083 if (wb->new_channel > 2)
2085 wb->new_channel -= 2;
2087 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2089 wb->data608->channel=wb->new_channel;
2090 if (wb->data608->channel!=cc_channel)
2093 // For lo values between 0x20-0x3f
2097 hb_log ("\rExtended: %02X %02X\n",hi,lo);
2098 if (lo>=0x20 && lo<=0x3f && (hi==0x12 || hi==0x13))
2103 c=lo+0x70; // So if c>=0x90 && c<=0xaf it comes from here
2106 c=lo+0x90; // So if c>=0xb0 && c<=0xcf it comes from here
2109 // This column change is because extended characters replace
2110 // the previous character (which is sent for basic decoders
2111 // to show something similar to the real char)
2112 if (wb->data608->cursor_column>0)
2113 wb->data608->cursor_column--;
2120 /* Process PREAMBLE ACCESS CODES (PAC) */
2121 void handle_pac (unsigned char c1, unsigned char c2, struct s_write *wb)
2123 // Handle channel change
2124 if (wb->new_channel > 2)
2126 wb->new_channel -= 2;
2128 hb_log ("\nChannel correction, now %d\n", wb->new_channel);
2130 wb->data608->channel=wb->new_channel;
2131 if (wb->data608->channel!=cc_channel)
2134 int row=rowdata[((c1<<1)&14)|((c2>>5)&1)];
2137 hb_log ("\rPAC: %02X %02X",c1,c2);
2139 if (c2>=0x40 && c2<=0x5f)
2145 if (c2>=0x60 && c2<=0x7f)
2152 hb_log ("\rThis is not a PAC!!!!!\n");
2156 int color=pac2_attribs[c2][0];
2157 int font=pac2_attribs[c2][1];
2158 int indent=pac2_attribs[c2][2];
2160 hb_log (" -- Position: %d:%d, color: %s, font: %s\n",row,
2161 indent,color_text[color][0],font_text[font]);
2162 if (wb->data608->mode!=MODE_TEXT)
2164 // According to Robson, row info is discarded in text mode
2165 // but column is accepted
2166 wb->data608->cursor_row=row-1 ; // Since the array is 0 based
2168 wb->data608->rollup_base_row=row-1;
2169 wb->data608->cursor_column=indent;
2173 void handle_single (const unsigned char c1, struct s_write *wb)
2175 if (c1<0x20 || wb->data608->channel!=cc_channel)
2176 return; // We don't allow special stuff here
2181 hb_log ("Character: %02X (%c) -> %02X (%c)\n",c1,c1,c,c); */
2185 int check_channel (unsigned char c1, struct s_write *wb)
2189 if (debug_608 && wb->data608->channel!=1)
2190 hb_log ("\nChannel change, now 1\n");
2195 if (debug_608 && wb->data608->channel!=2)
2196 hb_log ("\nChannel change, now 2\n");
2201 if (debug_608 && wb->data608->channel!=3)
2202 hb_log ("\nChannel change, now 3\n");
2207 if (debug_608 && wb->data608->channel!=4)
2208 hb_log ("\nChannel change, now 4\n");
2212 // Otherwise keep the current channel
2213 return wb->data608->channel;
2216 /* Handle Command, special char or attribute and also check for
2218 * Returns 1 if something was written to screen, 0 otherwise */
2219 int disCommand (unsigned char hi, unsigned char lo, struct s_write *wb)
2221 int wrote_to_screen=0;
2223 /* Full channel changes are only allowed for "GLOBAL CODES",
2224 * "OTHER POSITIONING CODES", "BACKGROUND COLOR CODES",
2226 * "PREAMBLE ACCESS CODES", "BACKGROUND COLOR CODES" and
2227 * SPECIAL/SPECIAL CHARACTERS allow only switching
2228 * between 1&3 or 2&4. */
2229 wb->new_channel = check_channel (hi,wb);
2230 //if (wb->data608->channel!=cc_channel)
2233 if (hi>=0x18 && hi<=0x1f)
2239 if (lo>=0x40 && lo<=0x5f)
2240 handle_pac (hi,lo,wb);
2243 if (lo>=0x20 && lo<=0x2f)
2244 handle_text_attr (hi,lo,wb);
2245 if (lo>=0x30 && lo<=0x3f)
2248 handle_double (hi,lo,wb);
2250 if (lo>=0x40 && lo<=0x7f)
2251 handle_pac (hi,lo,wb);
2255 if (lo>=0x20 && lo<=0x3f)
2257 wrote_to_screen=handle_extended (hi,lo,wb);
2259 if (lo>=0x40 && lo<=0x7f)
2260 handle_pac (hi,lo,wb);
2264 if (lo>=0x20 && lo<=0x2f)
2265 handle_command (hi,lo,wb);
2266 if (lo>=0x40 && lo<=0x7f)
2267 handle_pac (hi,lo,wb);
2270 if (lo>=0x40 && lo<=0x7f)
2271 handle_pac (hi,lo,wb);
2274 if (lo>=0x21 && lo<=0x22)
2275 handle_command (hi,lo,wb);
2276 if (lo>=0x2e && lo<=0x2f)
2277 handle_text_attr (hi,lo,wb);
2278 if (lo>=0x40 && lo<=0x7f)
2279 handle_pac (hi,lo,wb);
2282 return wrote_to_screen;
2285 void process608 (const unsigned char *data, int length, struct s_write *wb)
2287 static int textprinted = 0;
2292 for (i=0;i<length;i=i+2)
2294 unsigned char hi, lo;
2295 int wrote_to_screen=0;
2296 hi = data[i] & 0x7F; // Get rid of parity bit
2297 lo = data[i+1] & 0x7F; // Get rid of parity bit
2299 if (hi==0 && lo==0) // Just padding
2301 // hb_log ("\r[%02X:%02X]\n",hi,lo);
2303 if (hi>=0x01 && hi<=0x0E)
2305 // XDS crap - mode. Would be nice to support it eventually
2306 // wb->data608->last_c1=0;
2307 // wb->data608->last_c2=0;
2308 wb->data608->channel=3; // Always channel 3
2311 if (hi==0x0F) // End of XDS block
2316 if (hi>=0x10 && hi<0x1F) // Non-character code or special/extended char
2317 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML
2318 // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML
2320 // We were writing characters before, start a new line for
2321 // diagnostic output from disCommand()
2322 if (debug_608 && textprinted == 1 )
2328 wb->in_xds_mode=0; // Back to normal
2329 if (wb->data608->last_c1==hi && wb->data608->last_c2==lo)
2331 // Duplicate dual code, discard
2334 wb->data608->last_c1=hi;
2335 wb->data608->last_c2=lo;
2336 wrote_to_screen=disCommand (hi,lo,wb);
2338 if (hi>=0x20) // Standard characters (always in pairs)
2340 // Only print if the channel is active
2341 if (wb->data608->channel!=cc_channel)
2346 if( textprinted == 0 )
2353 handle_single(hi,wb);
2354 handle_single(lo,wb);
2356 wb->data608->last_c1=0;
2357 wb->data608->last_c2=0;
2360 if ( debug_608 && !textprinted && wb->data608->channel==cc_channel )
2361 { // Current FTS information after the characters are shown
2362 //hb_log("Current FTS: %s\n", print_mstime(get_fts()));
2365 if (wrote_to_screen && direct_rollup && // If direct_rollup is enabled and
2366 (wb->data608->mode==MODE_ROLLUP_2 || // we are in rollup mode, write now.
2367 wb->data608->mode==MODE_ROLLUP_3 ||
2368 wb->data608->mode==MODE_ROLLUP_4))
2370 // We don't increase screenfuls_counter here.
2371 write_cc_buffer (wb);
2372 wb->data608->current_visible_start_ms=get_fts();
2379 /* Return a pointer to a string that holds the printable characters
2380 * of the caption data block. FOR DEBUG PURPOSES ONLY! */
2381 unsigned char *debug_608toASC (unsigned char *cc_data, int channel)
2383 static unsigned char output[3];
2385 unsigned char cc_valid = (cc_data[0] & 4) >>2;
2386 unsigned char cc_type = cc_data[0] & 3;
2387 unsigned char hi, lo;
2393 if (cc_valid && cc_type==channel)
2395 hi = cc_data[1] & 0x7F; // Get rid of parity bit
2396 lo = cc_data[2] & 0x7F; // Get rid of parity bit
2400 output[1]=(lo>=20 ? lo : '.');