[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / video / Teletext.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 /*
22  * Most Codeparts are taken from the TuxBox Teletext plugin which is based
23  * upon videotext-0.6.19991029 and written by Thomas Loewe (LazyT),
24  * Roland Meier and DBLuelle. See http://www.tuxtxt.net/ for more information.
25  * Many thanks to the TuxBox Teletext Team for this great work.
26  */
27
28 #include "threads/SystemClock.h"
29 #include "Teletext.h"
30 #include "Application.h"
31 #include "utils/log.h"
32 #include "utils/TimeUtils.h"
33 #include "filesystem/SpecialProtocol.h"
34 #include "guilib/GraphicContext.h"
35
36 #ifdef HAS_SDL
37 #include <SDL/SDL_stdinc.h>
38 #else
39 #define SDL_memset4(dst, val, len)              \
40 do {                                            \
41         uint32_t _count = (len);                \
42         uint32_t _n = (_count + 3) / 4;         \
43         uint32_t *_p = static_cast<uint32_t *>(dst);    \
44         uint32_t _val = (val);                  \
45         if (len == 0) break;                    \
46         switch (_count % 4) {                   \
47         case 0: do {    *_p++ = _val;           \
48         case 3:         *_p++ = _val;           \
49         case 2:         *_p++ = _val;           \
50         case 1:         *_p++ = _val;           \
51                 } while ( --_n );               \
52         }                                       \
53 } while(0)
54 #define SDL_memcpy4(dst, src, len) memcpy(dst, src, (len) << 2)
55 #endif
56
57 using namespace std;
58
59 static const char *TeletextFont = "special://xbmc/media/Fonts/teletext.ttf";
60
61 /* spacing attributes */
62 #define alpha_black         0x00
63 #define alpha_red           0x01
64 #define alpha_green         0x02
65 #define alpha_yellow        0x03
66 #define alpha_blue          0x04
67 #define alpha_magenta       0x05
68 #define alpha_cyan          0x06
69 #define alpha_white         0x07
70 #define flash               0x08
71 #define steady              0x09
72 #define end_box             0x0A
73 #define start_box           0x0B
74 #define normal_size         0x0C
75 #define double_height       0x0D
76 #define double_width        0x0E
77 #define double_size         0x0F
78 #define mosaic_black        0x10
79 #define mosaic_red          0x11
80 #define mosaic_green        0x12
81 #define mosaic_yellow       0x13
82 #define mosaic_blue         0x14
83 #define mosaic_magenta      0x15
84 #define mosaic_cyan         0x16
85 #define mosaic_white        0x17
86 #define conceal             0x18
87 #define contiguous_mosaic   0x19
88 #define separated_mosaic    0x1A
89 #define esc                 0x1B
90 #define black_background    0x1C
91 #define new_background      0x1D
92 #define hold_mosaic         0x1E
93 #define release_mosaic      0x1F
94
95 #define RowAddress2Row(row) ((row == 40) ? 24 : (row - 40))
96
97 // G2 Set as defined in ETS 300 706
98 const unsigned short int G2table[5][6*16] =
99 {
100   // Latin G2 Supplementary Set
101   { 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x0024, 0x00A5, 0x0023, 0x00A7, 0x00A4, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193,
102     0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
103     0x0020, 0x0300, 0x0301, 0x02C6, 0x0303, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7,
104     0x2014, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x0020, 0x0020, 0x0020, 0x215B, 0x215C, 0x215D, 0x215E,
105     0x2126, 0x00C6, 0x00D0, 0x00AA, 0x0126, 0x0020, 0x0132, 0x013F, 0x0141, 0x00D8, 0x0152, 0x00BA, 0x00DE, 0x0166, 0x014A, 0x0149,
106     0x0138, 0x00E6, 0x0111, 0x00F0, 0x0127, 0x0131, 0x0133, 0x0140, 0x0142, 0x00F8, 0x0153, 0x00DF, 0x00FE, 0x0167, 0x014B, 0x25A0},
107   // Cyrillic G2 Supplementary Set
108   { 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x0024, 0x00A5, 0x0020, 0x00A7, 0x0020, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193,
109     0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
110     0x0020, 0x0300, 0x0301, 0x02C6, 0x02DC, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7,
111     0x2014, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x0141, 0x0142, 0x00DF, 0x215B, 0x215C, 0x215D, 0x215E,
112     0x0044, 0x0045, 0x0046, 0x0047, 0x0049, 0x004A, 0x004B, 0x004C, 0x004E, 0x0051, 0x0052, 0x0053, 0x0055, 0x0056, 0x0057, 0x005A,
113     0x0064, 0x0065, 0x0066, 0x0067, 0x0069, 0x006A, 0x006B, 0x006C, 0x006E, 0x0071, 0x0072, 0x0073, 0x0075, 0x0076, 0x0077, 0x007A},
114   // Greek G2 Supplementary Set
115   { 0x0020, 0x0061, 0x0062, 0x00A3, 0x0065, 0x0068, 0x0069, 0x00A7, 0x003A, 0x2018, 0x201C, 0x006B, 0x2190, 0x2191, 0x2192, 0x2193,
116     0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x006D, 0x006E, 0x0070, 0x00F7, 0x2019, 0x201D, 0x0074, 0x00BC, 0x00BD, 0x00BE, 0x0078,
117     0x0020, 0x0300, 0x0301, 0x02C6, 0x02DC, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7,
118     0x003F, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x038A, 0x038E, 0x038F, 0x215B, 0x215C, 0x215D, 0x215E,
119     0x0043, 0x0044, 0x0046, 0x0047, 0x004A, 0x004C, 0x0051, 0x0052, 0x0053, 0x0055, 0x0056, 0x0057, 0x0059, 0x005A, 0x0386, 0x0389,
120     0x0063, 0x0064, 0x0066, 0x0067, 0x006A, 0x006C, 0x0071, 0x0072, 0x0073, 0x0075, 0x0076, 0x0077, 0x0079, 0x007A, 0x0388, 0x25A0},
121   // Arabic G2 Set
122   { 0x0020, 0x0639, 0xFEC9, 0xFE83, 0xFE85, 0xFE87, 0xFE8B, 0xFE89, 0xFB7C, 0xFB7D, 0xFB7A, 0xFB58, 0xFB59, 0xFB56, 0xFB6D, 0xFB8E,
123     0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFECE, 0xFECD, 0xFEFC, 0xFEEC, 0xFEEA, 0xFEE9,
124     0x00E0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
125     0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00EB, 0x00EA, 0x00F9, 0x00EE, 0xFECA,
126     0x00E9, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
127     0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E2, 0x00F4, 0x00FB, 0x00E7, 0x25A0}
128 };
129
130 //const (avoid warnings :<)
131 TextPageAttr_t Text_AtrTable[] =
132 {
133   { TXT_ColorWhite  , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_WB */
134   { TXT_ColorWhite  , TXT_ColorBlack , C_G0P, 0, 0, 1 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_PassiveDefault */
135   { TXT_ColorWhite  , TXT_ColorRed   , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L250 */
136   { TXT_ColorBlack  , TXT_ColorYellow, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L252 */
137   { TXT_ColorBlack  , TXT_ColorGreen , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L251 */
138   { TXT_ColorWhite  , TXT_ColorBlue  , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L253 */
139   { TXT_ColorMagenta, TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU0 */
140   { TXT_ColorGreen  , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU1 */
141   { TXT_ColorYellow , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU2 */
142   { TXT_ColorCyan   , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU3 */
143   { TXT_ColorMenu2  , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG0 */
144   { TXT_ColorYellow , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG1 */
145   { TXT_ColorMenu2  , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG2 */
146   { TXT_ColorWhite  , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG3 */
147   { TXT_ColorMenu2  , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM0 */
148   { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM1 */
149   { TXT_ColorMenu2  , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM2 */
150   { TXT_ColorWhite  , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM3 */
151   { TXT_ColorMenu1  , TXT_ColorBlue  , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL0 5a Z */
152   { TXT_ColorWhite  , TXT_ColorBlue  , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL1 58 X */
153   { TXT_ColorMenu2  , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL2 9b õ */
154   { TXT_ColorMenu2  , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU0 ab ´ */
155   { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU1 a4 § */
156   { TXT_ColorMenu2  , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU2 9b õ */
157   { TXT_ColorMenu2  , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU3 cb À */
158   { TXT_ColorCyan   , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU4 c7 « */
159   { TXT_ColorWhite  , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU5 c8 » */
160   { TXT_ColorWhite  , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU6 a8 ® */
161   { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_CATCHMENU0 a4 § */
162   { TXT_ColorWhite  , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}  /* ATR_CATCHMENU1 a8 ® */
163 };
164
165 /* shapes */
166 enum
167 {
168   S_END = 0,
169   S_FHL, /* full horizontal line: y-offset */
170   S_FVL, /* full vertical line: x-offset */
171   S_BOX, /* rectangle: x-offset, y-offset, width, height */
172   S_TRA, /* trapez: x0, y0, l0, x1, y1, l1 */
173   S_BTR, /* trapez in bgcolor: x0, y0, l0, x1, y1, l1 */
174   S_INV, /* invert */
175   S_LNK, /* call other shape: shapenumber */
176   S_CHR, /* Character from freetype hibyte, lowbyte */
177   S_ADT, /* Character 2F alternating raster */
178   S_FLH, /* flip horizontal */
179   S_FLV  /* flip vertical */
180 };
181
182 /* shape coordinates */
183 enum
184 {
185   S_W13 = 5, /* width*1/3 */
186   S_W12, /* width*1/2 */
187   S_W23, /* width*2/3 */
188   S_W11, /* width */
189   S_WM3, /* width-3 */
190   S_H13, /* height*1/3 */
191   S_H12, /* height*1/2 */
192   S_H23, /* height*2/3 */
193   S_H11, /* height */
194   S_NrShCoord
195 };
196
197 /* G3 characters */
198 unsigned char aG3_20[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W12, S_END };
199 unsigned char aG3_21[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W11, S_END };
200 unsigned char aG3_22[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W12, S_END };
201 unsigned char aG3_23[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W11, S_END };
202 unsigned char aG3_24[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W12, S_END };
203 unsigned char aG3_25[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W11, S_END };
204 unsigned char aG3_26[] = { S_INV, S_LNK, 0x66, S_END };
205 unsigned char aG3_27[] = { S_INV, S_LNK, 0x67, S_END };
206 unsigned char aG3_28[] = { S_INV, S_LNK, 0x68, S_END };
207 unsigned char aG3_29[] = { S_INV, S_LNK, 0x69, S_END };
208 unsigned char aG3_2a[] = { S_INV, S_LNK, 0x6a, S_END };
209 unsigned char aG3_2b[] = { S_INV, S_LNK, 0x6b, S_END };
210 unsigned char aG3_2c[] = { S_INV, S_LNK, 0x6c, S_END };
211 unsigned char aG3_2d[] = { S_INV, S_LNK, 0x6d, S_END };
212 unsigned char aG3_2e[] = { S_BOX, 2, 0, 3, S_H11, S_END };
213 unsigned char aG3_2f[] = { S_ADT };
214 unsigned char aG3_30[] = { S_LNK, 0x20, S_FLH, S_END };
215 unsigned char aG3_31[] = { S_LNK, 0x21, S_FLH, S_END };
216 unsigned char aG3_32[] = { S_LNK, 0x22, S_FLH, S_END };
217 unsigned char aG3_33[] = { S_LNK, 0x23, S_FLH, S_END };
218 unsigned char aG3_34[] = { S_LNK, 0x24, S_FLH, S_END };
219 unsigned char aG3_35[] = { S_LNK, 0x25, S_FLH, S_END };
220 unsigned char aG3_36[] = { S_INV, S_LNK, 0x76, S_END };
221 unsigned char aG3_37[] = { S_INV, S_LNK, 0x77, S_END };
222 unsigned char aG3_38[] = { S_INV, S_LNK, 0x78, S_END };
223 unsigned char aG3_39[] = { S_INV, S_LNK, 0x79, S_END };
224 unsigned char aG3_3a[] = { S_INV, S_LNK, 0x7a, S_END };
225 unsigned char aG3_3b[] = { S_INV, S_LNK, 0x7b, S_END };
226 unsigned char aG3_3c[] = { S_INV, S_LNK, 0x7c, S_END };
227 unsigned char aG3_3d[] = { S_INV, S_LNK, 0x7d, S_END };
228 unsigned char aG3_3e[] = { S_LNK, 0x2e, S_FLH, S_END };
229 unsigned char aG3_3f[] = { S_BOX, 0, 0, S_W11, S_H11, S_END };
230 unsigned char aG3_40[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_END };
231 unsigned char aG3_41[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_FLV, S_END };
232 unsigned char aG3_42[] = { S_LNK, 0x50, S_BOX, S_W12, S_H13, S_W12, S_H13, S_END };
233 unsigned char aG3_43[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W12, S_H13, S_END };
234 unsigned char aG3_44[] = { S_LNK, 0x48, S_FLV, S_LNK, 0x48, S_END };
235 unsigned char aG3_45[] = { S_LNK, 0x44, S_FLH, S_END };
236 unsigned char aG3_46[] = { S_LNK, 0x47, S_FLV, S_END };
237 unsigned char aG3_47[] = { S_LNK, 0x48, S_FLH, S_LNK, 0x48, S_END };
238 unsigned char aG3_48[] = { S_TRA, 0, 0, S_W23, 0, S_H23, 0, S_BTR, 0, 0, S_W13, 0, S_H13, 0, S_END };
239 unsigned char aG3_49[] = { S_LNK, 0x48, S_FLH, S_END };
240 unsigned char aG3_4a[] = { S_LNK, 0x48, S_FLV, S_END };
241 unsigned char aG3_4b[] = { S_LNK, 0x48, S_FLH, S_FLV, S_END };
242 unsigned char aG3_4c[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W11, S_H13, S_END };
243 unsigned char aG3_4d[] = { S_CHR, 0x25, 0xE6 };
244 unsigned char aG3_4e[] = { S_CHR, 0x25, 0xCF };
245 unsigned char aG3_4f[] = { S_CHR, 0x25, 0xCB };
246 unsigned char aG3_50[] = { S_BOX, S_W12, 0, 2, S_H11, S_FLH, S_BOX, S_W12, 0, 2, S_H11,S_END };
247 unsigned char aG3_51[] = { S_BOX, 0, S_H12, S_W11, 2, S_FLV, S_BOX, 0, S_H12, S_W11, 2,S_END };
248 unsigned char aG3_52[] = { S_LNK, 0x55, S_FLH, S_FLV, S_END };
249 unsigned char aG3_53[] = { S_LNK, 0x55, S_FLV, S_END };
250 unsigned char aG3_54[] = { S_LNK, 0x55, S_FLH, S_END };
251 unsigned char aG3_55[] = { S_LNK, 0x7e, S_FLV, S_BOX, 0, S_H12, S_W12, 2, S_FLV, S_BOX, 0, S_H12, S_W12, 2, S_END };
252 unsigned char aG3_56[] = { S_LNK, 0x57, S_FLH, S_END};
253 unsigned char aG3_57[] = { S_LNK, 0x55, S_LNK, 0x50 , S_END};
254 unsigned char aG3_58[] = { S_LNK, 0x59, S_FLV, S_END};
255 unsigned char aG3_59[] = { S_LNK, 0x7e, S_LNK, 0x51 , S_END};
256 unsigned char aG3_5a[] = { S_LNK, 0x50, S_LNK, 0x51 , S_END};
257 unsigned char aG3_5b[] = { S_CHR, 0x21, 0x92};
258 unsigned char aG3_5c[] = { S_CHR, 0x21, 0x90};
259 unsigned char aG3_5d[] = { S_CHR, 0x21, 0x91};
260 unsigned char aG3_5e[] = { S_CHR, 0x21, 0x93};
261 unsigned char aG3_5f[] = { S_CHR, 0x00, 0x20};
262 unsigned char aG3_60[] = { S_INV, S_LNK, 0x20, S_END };
263 unsigned char aG3_61[] = { S_INV, S_LNK, 0x21, S_END };
264 unsigned char aG3_62[] = { S_INV, S_LNK, 0x22, S_END };
265 unsigned char aG3_63[] = { S_INV, S_LNK, 0x23, S_END };
266 unsigned char aG3_64[] = { S_INV, S_LNK, 0x24, S_END };
267 unsigned char aG3_65[] = { S_INV, S_LNK, 0x25, S_END };
268 unsigned char aG3_66[] = { S_LNK, 0x20, S_FLV, S_END };
269 unsigned char aG3_67[] = { S_LNK, 0x21, S_FLV, S_END };
270 unsigned char aG3_68[] = { S_LNK, 0x22, S_FLV, S_END };
271 unsigned char aG3_69[] = { S_LNK, 0x23, S_FLV, S_END };
272 unsigned char aG3_6a[] = { S_LNK, 0x24, S_FLV, S_END };
273 unsigned char aG3_6b[] = { S_BOX, 0, 0, S_W11, S_H13, S_TRA, 0, S_H13, S_W11, 0, S_H23, 1, S_END };
274 unsigned char aG3_6c[] = { S_TRA, 0, 0, 1, 0, S_H12, S_W12, S_FLV, S_TRA, 0, 0, 1, 0, S_H12, S_W12, S_BOX, 0, S_H12, S_W12,1, S_END };
275 unsigned char aG3_6d[] = { S_TRA, 0, 0, S_W12, S_W12, S_H12, 0, S_FLH, S_TRA, 0, 0, S_W12, S_W12, S_H12, 0, S_END };
276 unsigned char aG3_6e[] = { S_CHR, 0x00, 0x20};
277 unsigned char aG3_6f[] = { S_CHR, 0x00, 0x20};
278 unsigned char aG3_70[] = { S_INV, S_LNK, 0x30, S_END };
279 unsigned char aG3_71[] = { S_INV, S_LNK, 0x31, S_END };
280 unsigned char aG3_72[] = { S_INV, S_LNK, 0x32, S_END };
281 unsigned char aG3_73[] = { S_INV, S_LNK, 0x33, S_END };
282 unsigned char aG3_74[] = { S_INV, S_LNK, 0x34, S_END };
283 unsigned char aG3_75[] = { S_INV, S_LNK, 0x35, S_END };
284 unsigned char aG3_76[] = { S_LNK, 0x66, S_FLH, S_END };
285 unsigned char aG3_77[] = { S_LNK, 0x67, S_FLH, S_END };
286 unsigned char aG3_78[] = { S_LNK, 0x68, S_FLH, S_END };
287 unsigned char aG3_79[] = { S_LNK, 0x69, S_FLH, S_END };
288 unsigned char aG3_7a[] = { S_LNK, 0x6a, S_FLH, S_END };
289 unsigned char aG3_7b[] = { S_LNK, 0x6b, S_FLH, S_END };
290 unsigned char aG3_7c[] = { S_LNK, 0x6c, S_FLH, S_END };
291 unsigned char aG3_7d[] = { S_LNK, 0x6d, S_FLV, S_END };
292 unsigned char aG3_7e[] = { S_BOX, S_W12, 0, 2, S_H12, S_FLH, S_BOX, S_W12, 0, 2, S_H12, S_END };// help char, not printed directly (only by S_LNK)
293
294 unsigned char *aShapes[] =
295 {
296   aG3_20, aG3_21, aG3_22, aG3_23, aG3_24, aG3_25, aG3_26, aG3_27, aG3_28, aG3_29, aG3_2a, aG3_2b, aG3_2c, aG3_2d, aG3_2e, aG3_2f,
297   aG3_30, aG3_31, aG3_32, aG3_33, aG3_34, aG3_35, aG3_36, aG3_37, aG3_38, aG3_39, aG3_3a, aG3_3b, aG3_3c, aG3_3d, aG3_3e, aG3_3f,
298   aG3_40, aG3_41, aG3_42, aG3_43, aG3_44, aG3_45, aG3_46, aG3_47, aG3_48, aG3_49, aG3_4a, aG3_4b, aG3_4c, aG3_4d, aG3_4e, aG3_4f,
299   aG3_50, aG3_51, aG3_52, aG3_53, aG3_54, aG3_55, aG3_56, aG3_57, aG3_58, aG3_59, aG3_5a, aG3_5b, aG3_5c, aG3_5d, aG3_5e, aG3_5f,
300   aG3_60, aG3_61, aG3_62, aG3_63, aG3_64, aG3_65, aG3_66, aG3_67, aG3_68, aG3_69, aG3_6a, aG3_6b, aG3_6c, aG3_6d, aG3_6e, aG3_6f,
301   aG3_70, aG3_71, aG3_72, aG3_73, aG3_74, aG3_75, aG3_76, aG3_77, aG3_78, aG3_79, aG3_7a, aG3_7b, aG3_7c, aG3_7d, aG3_7e
302 };
303
304 // G0 Table as defined in ETS 300 706
305 // cyrillic G0 Charset (0 = Serbian/Croatian, 1 = Russian/Bulgarian, 2 = Ukrainian)
306 const unsigned short int G0table[6][6*16] =
307 {
308   // Cyrillic G0 Set - Option 1 - Serbian/Croatian
309   { ' ', '!', '\"', '#', '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
310     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
311     0x0427, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0408, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
312     0x041F, 0x040C, 0x0420, 0x0421, 0x0422, 0x0423, 0x0412, 0x0403, 0x0409, 0x040A, 0x0417, 0x040B, 0x0416, 0x0402, 0x0428, 0x040F,
313     0x0447, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0458, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
314     0x043F, 0x045C, 0x0440, 0x0441, 0x0442, 0x0443, 0x0432, 0x0453, 0x0459, 0x045A, 0x0437, 0x045B, 0x0436, 0x0452, 0x0448, 0x25A0},
315   // Cyrillic G0 Set - Option 2 - Russian/Bulgarian
316   { ' ', '!', '\"', '#', '$', '%', 0x044B, '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
317     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
318     0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
319     0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042A, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042B,
320     0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
321     0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044A, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x25A0},
322   // Cyrillic G0 Set - Option 3 - Ukrainian
323   { ' ', '!', '\"', '#', '$', '%', 0x0457, '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
324     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
325     0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
326     0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x0406, 0x0417, 0x0428, 0x0404, 0x0429, 0x0427, 0x0407,
327     0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
328     0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x0456, 0x0437, 0x0448, 0x0454, 0x0449, 0x0447, 0x25A0},
329   // Greek G0 Set
330   { ' ', '!', '\"', '#', '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
331     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', 0x00AB, '=', 0x00BB, '?',
332     0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
333     0x03A0, 0x03A1, 0x0384, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
334     0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
335     0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x25A0},
336   // Hebrew G0 Set
337   { ' ', '!', 0x05F2, 0x00A3, '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
338     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
339     '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
340     'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x2190, 0x00BD, 0x2192, 0x2191, '#',
341     0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
342     0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x20AA, 0x2551, 0x00BE, 0x00F7, 0x25A0},
343   // Arabic G0 Set - Thanks to Habib2006(fannansat)
344   { ' ', '!', 0x05F2, 0x00A3, '$', 0x066A, 0xFEF0, 0xFEF2, 0xFD3F, 0xFD3E, '*', '+', ',', '-', '.', '/',
345     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', 0x061B, '>', '=', '<', 0x061F,
346     0xFE94, 0x0621, 0xFE92, 0x0628, 0xFE98, 0x062A, 0xFE8E, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9,
347     0x0630, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0xFE9C, 0xFEA0, 0xFEA4, 0xFEA8, 0x0023,
348     0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFE99, 0xFE9D, 0xFEA1, 0xFEA5, 0xFEF4,
349     0xFEF0, 0xFECC, 0xFED0, 0xFED4, 0xFED1, 0xFED8, 0xFED5, 0xFED9, 0xFEE0, 0xFEDD, 0xFEE4, 0xFEE1, 0xFEE8, 0xFEE5, 0xFEFB, 0x25A0}
350 };
351
352 const unsigned short int nationaltable23[14][2] =
353 {
354   { '#',    0x00A4 }, /* 0          */
355   { '#',    0x016F }, /* 1  CS/SK   */
356   { 0x00A3,    '$' }, /* 2    EN    */
357   { '#',    0x00F5 }, /* 3    ET    */
358   { 0x00E9, 0x0457 }, /* 4    FR    */
359   { '#',       '$' }, /* 5    DE    */
360   { 0x00A3,    '$' }, /* 6    IT    */
361   { '#',       '$' }, /* 7  LV/LT   */
362   { '#',    0x0144 }, /* 8    PL    */
363   { 0x00E7,    '$' }, /* 9  PT/ES   */
364   { '#',    0x00A4 }, /* A    RO    */
365   { '#',    0x00CB }, /* B SR/HR/SL */
366   { '#',    0x00A4 }, /* C SV/FI/HU */
367   { 0x20A4, 0x011F }, /* D    TR    */
368 };
369 const unsigned short int nationaltable40[14] =
370 {
371   '@',    /* 0          */
372   0x010D, /* 1  CS/SK   */
373   '@',    /* 2    EN    */
374   0x0161, /* 3    ET    */
375   0x00E0, /* 4    FR    */
376   0x00A7, /* 5    DE    */
377   0x00E9, /* 6    IT    */
378   0x0161, /* 7  LV/LT   */
379   0x0105, /* 8    PL    */
380   0x00A1, /* 9  PT/ES   */
381   0x0162, /* A    RO    */
382   0x010C, /* B SR/HR/SL */
383   0x00C9, /* C SV/FI/HU */
384   0x0130, /* D    TR    */
385 };
386 const unsigned short int nationaltable5b[14][6] =
387 {
388   {    '[',   '\\',    ']',    '^',    '_',    '`' }, /* 0          */
389   { 0x0165, 0x017E, 0x00FD, 0x00ED, 0x0159, 0x00E9 }, /* 1  CS/SK   */
390   { 0x2190, 0x00BD, 0x2192, 0x2191,    '#', 0x00AD }, /* 2    EN    */
391   { 0x00C4, 0x00D6, 0x017D, 0x00DC, 0x00D5, 0x0161 }, /* 3    ET    */
392   { 0x0451, 0x00EA, 0x00F9, 0x00EE,    '#', 0x00E8 }, /* 4    FR    */
393   { 0x00C4, 0x00D6, 0x00DC,    '^',    '_', 0x00B0 }, /* 5    DE    */
394   { 0x00B0, 0x00E7, 0x2192, 0x2191,    '#', 0x00F9 }, /* 6    IT    */
395   { 0x0117, 0x0119, 0x017D, 0x010D, 0x016B, 0x0161 }, /* 7  LV/LT   */
396   { 0x017B, 0x015A, 0x0141, 0x0107, 0x00F3, 0x0119 }, /* 8    PL    */
397   { 0x00E1, 0x00E9, 0x00ED, 0x00F3, 0x00FA, 0x00BF }, /* 9  PT/ES   */
398   { 0x00C2, 0x015E, 0x01CD, 0x01CF, 0x0131, 0x0163 }, /* A    RO    */
399   { 0x0106, 0x017D, 0x00D0, 0x0160, 0x0451, 0x010D }, /* B SR/HR/SL */
400   { 0x00C4, 0x00D6, 0x00C5, 0x00DC,    '_', 0x00E9 }, /* C SV/FI/HU */
401   { 0x015E, 0x00D6, 0x00C7, 0x00DC, 0x011E, 0x0131 }, /* D    TR    */
402 };
403 const unsigned short int nationaltable7b[14][4] =
404 {
405   { '{',       '|',    '}',    '~' }, /* 0          */
406   { 0x00E1, 0x011B, 0x00FA, 0x0161 }, /* 1  CS/SK   */
407   { 0x00BC, 0x2551, 0x00BE, 0x00F7 }, /* 2    EN    */
408   { 0x00E4, 0x00F6, 0x017E, 0x00FC }, /* 3    ET    */
409   { 0x00E2, 0x00F4, 0x00FB, 0x00E7 }, /* 4    FR    */
410   { 0x00E4, 0x00F6, 0x00FC, 0x00DF }, /* 5    DE    */
411   { 0x00E0, 0x00F3, 0x00E8, 0x00EC }, /* 6    IT    */
412   { 0x0105, 0x0173, 0x017E, 0x012F }, /* 7  LV/LT   */
413   { 0x017C, 0x015B, 0x0142, 0x017A }, /* 8    PL    */
414   { 0x00FC, 0x00F1, 0x00E8, 0x00E0 }, /* 9  PT/ES   */
415   { 0x00E2, 0x015F, 0x01CE, 0x00EE }, /* A    RO    */
416   { 0x0107, 0x017E, 0x0111, 0x0161 }, /* B SR/HR/SL */
417   { 0x00E4, 0x00F6, 0x00E5, 0x00FC }, /* C SV/FI/HU */
418   { 0x015F, 0x00F6, 0x00E7, 0x00FC }, /* D    TR    */
419 };
420 const unsigned short int arrowtable[] =
421 {
422   8592, 8594, 8593, 8595, 'O', 'K', 8592, 8592
423 };
424
425 CTeletextDecoder::CTeletextDecoder()
426 {
427   memset(&m_RenderInfo, 0, sizeof(TextRenderInfo_t));
428
429   m_teletextFont                 = CSpecialProtocol::TranslatePath(TeletextFont);
430   m_TextureBuffer                = NULL;
431   m_txtCache                     = NULL;
432   m_Manager                      = NULL;
433   m_Library                      = NULL;
434   m_RenderInfo.ShowFlof          = true;
435   m_RenderInfo.Show39            = true;
436   m_RenderInfo.Showl25           = true;
437   m_RenderInfo.Prev_100          = 0x100;
438   m_RenderInfo.Prev_10           = 0x100;
439   m_RenderInfo.Next_100          = 0x100;
440   m_RenderInfo.Next_10           = 0x100;
441   m_RenderInfo.InputCounter      = 2;
442
443   unsigned short rd0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x00<<8, 0x00<<8, 0x00<<8, 0,      0      };
444   unsigned short gn0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x20<<8, 0x10<<8, 0x20<<8, 0,      0      };
445   unsigned short bl0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x40<<8, 0x20<<8, 0x40<<8, 0,      0      };
446   unsigned short tr0[] = {0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
447                           0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
448                           0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
449                           0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
450                           0x0000 , 0x0000 , 0x0A0A , 0xFFFF, 0x3030 };
451
452   memcpy(m_RenderInfo.rd0,rd0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
453   memcpy(m_RenderInfo.gn0,gn0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
454   memcpy(m_RenderInfo.bl0,bl0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
455   memcpy(m_RenderInfo.tr0,tr0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
456
457   m_LastPage = 0;
458   m_TempPage = 0;
459   m_Ascender = 0;
460   m_PCOldCol = 0;
461   m_PCOldRow = 0;
462   m_CatchedPage = 0;
463   m_CatchCol = 0;
464   m_CatchRow = 0;
465   prevTimeSec = 0;
466   prevHeaderPage = 0;
467   m_updateTexture = false;
468   m_YOffset = 0;
469 }
470
471 CTeletextDecoder::~CTeletextDecoder()
472 {
473 }
474
475 bool CTeletextDecoder::HandleAction(const CAction &action)
476 {
477   if (m_txtCache == NULL)
478   {
479     CLog::Log(LOGERROR, "CTeletextDecoder::HandleAction called without teletext cache");
480     return false;
481   }
482
483   if (action.GetID() == ACTION_MOVE_UP)
484   {
485     if (m_RenderInfo.PageCatching)
486       CatchNextPage(-1, -1);
487     else
488       GetNextPageOne(true);
489     return true;
490   }
491   else if (action.GetID() == ACTION_MOVE_DOWN)
492   {
493     if (m_RenderInfo.PageCatching)
494       CatchNextPage(1, 1);
495     else
496       GetNextPageOne(false);
497     return true;
498   }
499   else if (action.GetID() == ACTION_MOVE_RIGHT)
500   {
501     if (m_RenderInfo.PageCatching)
502       CatchNextPage(0, 1);
503     else if (m_RenderInfo.Boxed)
504     {
505       m_RenderInfo.SubtitleDelay++;
506           // display SubtitleDelay
507       m_RenderInfo.PosY = 0;
508       char ns[10];
509       SetPosX(1);
510       sprintf(ns,"+%d    ", m_RenderInfo.SubtitleDelay);
511       RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]);
512       RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]);
513       RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]);
514       RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]);
515     }
516     else
517     {
518       GetNextSubPage(1);
519     }
520     return true;
521   }
522   else if (action.GetID() == ACTION_MOVE_LEFT)
523   {
524     if (m_RenderInfo.PageCatching)
525       CatchNextPage(0, -1);
526     else if (m_RenderInfo.Boxed)
527     {
528         m_RenderInfo.SubtitleDelay--;
529
530         // display subtitledelay
531         m_RenderInfo.PosY = 0;
532         char ns[10];
533         SetPosX(1);
534         sprintf(ns,"+%d    ", m_RenderInfo.SubtitleDelay);
535         RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]);
536         RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]);
537         RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]);
538         RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]);
539     }
540     else
541     {
542       GetNextSubPage(-1);
543     }
544     return true;
545   }
546   else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9)
547   {
548     PageInput(action.GetID() - REMOTE_0);
549     return true;
550   }
551   else if (action.GetID() >= KEY_ASCII) // FIXME make it KEY_UNICODE
552   { // input from the keyboard
553     if (action.GetUnicode() >= 48 && action.GetUnicode() < 58)
554     {
555       PageInput(action.GetUnicode() - 48);
556       return true;
557     }
558     return false;
559   }
560   else if (action.GetID() == ACTION_PAGE_UP)
561   {
562     SwitchZoomMode();
563     return true;
564   }
565   else if (action.GetID() == ACTION_PAGE_DOWN)
566   {
567     SwitchTranspMode();
568     return true;
569   }
570   else if (action.GetID() == ACTION_SELECT_ITEM)
571   {
572     if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xFF)
573       return false;
574
575     if (!m_RenderInfo.PageCatching)
576       StartPageCatching();
577     else
578       StopPageCatching();
579
580     return true;
581   }
582
583   if (m_RenderInfo.PageCatching)
584   {
585     m_txtCache->PageUpdate    = true;
586     m_RenderInfo.PageCatching = 0;
587     return true;
588   }
589
590   if (action.GetID() == ACTION_SHOW_INFO)
591   {
592     SwitchHintMode();
593     return true;
594   }
595   else if (action.GetID() == ACTION_TELETEXT_RED)
596   {
597     ColorKey(m_RenderInfo.Prev_100);
598     return true;
599   }
600   else if (action.GetID() == ACTION_TELETEXT_GREEN)
601   {
602     ColorKey(m_RenderInfo.Prev_10);
603     return true;
604   }
605   else if (action.GetID() == ACTION_TELETEXT_YELLOW)
606   {
607     ColorKey(m_RenderInfo.Next_10);
608     return true;
609   }
610   else if (action.GetID() == ACTION_TELETEXT_BLUE)
611   {
612     ColorKey(m_RenderInfo.Next_100);
613     return true;
614   }
615
616   return false;
617 }
618
619 bool CTeletextDecoder::InitDecoder()
620 {
621   int error;
622
623   m_txtCache = g_application.m_pPlayer->GetTeletextCache();
624   if (m_txtCache == NULL)
625   {
626     CLog::Log(LOGERROR, "%s: called without teletext cache", __FUNCTION__);
627     return false;
628   }
629
630   /* init fontlibrary */
631   if ((error = FT_Init_FreeType(&m_Library)))
632   {
633     CLog::Log(LOGERROR, "%s: <FT_Init_FreeType: 0x%.2X>", __FUNCTION__, error);
634     m_Library = NULL;
635     return false;
636   }
637
638   if ((error = FTC_Manager_New(m_Library, 7, 2, 0, &MyFaceRequester, NULL, &m_Manager)))
639   {
640     FT_Done_FreeType(m_Library);
641     m_Library = NULL;
642     m_Manager = NULL;
643     CLog::Log(LOGERROR, "%s: <FTC_Manager_New: 0x%.2X>", __FUNCTION__, error);
644     return false;
645   }
646
647   if ((error = FTC_SBitCache_New(m_Manager, &m_Cache)))
648   {
649     FTC_Manager_Done(m_Manager);
650     FT_Done_FreeType(m_Library);
651     m_Manager = NULL;
652     m_Library = NULL;
653     CLog::Log(LOGERROR, "%s: <FTC_SBit_Cache_New: 0x%.2X>", __FUNCTION__, error);
654     return false;
655   }
656
657   /* calculate font dimensions */
658   m_RenderInfo.Width            = (int)(g_graphicsContext.GetWidth()*g_graphicsContext.GetGUIScaleX());
659   m_RenderInfo.Height           = (int)(g_graphicsContext.GetHeight()*g_graphicsContext.GetGUIScaleY());
660   m_RenderInfo.FontHeight       = m_RenderInfo.Height / 25;
661   m_RenderInfo.FontWidth_Normal = m_RenderInfo.Width  / (m_RenderInfo.Show39 ? 39 : 40);
662   SetFontWidth(m_RenderInfo.FontWidth_Normal);
663   for (int i = 0; i <= 10; i++)
664     m_RenderInfo.axdrcs[i+12+1] = (m_RenderInfo.FontHeight * i + 6) / 10;
665
666   /* center screen */
667   m_TypeTTF.face_id   = (FTC_FaceID) m_teletextFont.c_str();
668   m_TypeTTF.height    = (FT_UShort) m_RenderInfo.FontHeight;
669   m_TypeTTF.flags     = FT_LOAD_MONOCHROME;
670   if (FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face))
671   {
672     m_TypeTTF.face_id = (FTC_FaceID) m_teletextFont.c_str();
673     if ((error = FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face)))
674     {
675       CLog::Log(LOGERROR, "%s: <FTC_Manager_Lookup_Face failed with Errorcode 0x%.2X>\n", __FUNCTION__, error);
676       FTC_Manager_Done(m_Manager);
677       FT_Done_FreeType(m_Library);
678       m_Manager = NULL;
679       m_Library = NULL;
680       return false;
681     }
682   }
683   m_Ascender = m_RenderInfo.FontHeight * m_Face->ascender / m_Face->units_per_EM;
684
685   /* set variable screeninfo for double buffering */
686   m_YOffset       = 0;
687   m_TextureBuffer = new color_t [4*m_RenderInfo.Height*m_RenderInfo.Width];
688
689   ClearFB(GetColorRGB(TXT_ColorTransp));
690   ClearBB(GetColorRGB(TXT_ColorTransp)); /* initialize backbuffer */
691   /* set new colormap */
692   SetColors((unsigned short *)DefaultColors, 0, TXT_Color_SIZECOLTABLE);
693
694   for (int i = 0; i < 40 * 25; i++)
695   {
696     m_RenderInfo.PageChar[i]         = ' ';
697     m_RenderInfo.PageAtrb[i].fg      = TXT_ColorTransp;
698     m_RenderInfo.PageAtrb[i].bg      = TXT_ColorTransp;
699     m_RenderInfo.PageAtrb[i].charset = C_G0P;
700     m_RenderInfo.PageAtrb[i].doubleh = 0;
701     m_RenderInfo.PageAtrb[i].doublew = 0;
702     m_RenderInfo.PageAtrb[i].IgnoreAtBlackBgSubst = 0;
703   }
704
705   m_RenderInfo.TranspMode = false;
706   m_LastPage              = 0x100;
707
708   return true;
709 }
710
711 void CTeletextDecoder::EndDecoder()
712 {
713   /* clear SubtitleCache */
714   for (int i = 0; i < SUBTITLE_CACHESIZE; i++)
715   {
716     if (m_RenderInfo.SubtitleCache[i] != NULL)
717     {
718       delete m_RenderInfo.SubtitleCache[i];
719       m_RenderInfo.SubtitleCache[i] = NULL;
720     }
721   }
722
723   if (m_TextureBuffer)
724   {
725     delete[] m_TextureBuffer;
726     m_TextureBuffer = NULL;
727   }
728
729   /* close freetype */
730   if (m_Manager)
731   {
732     FTC_Manager_Done(m_Manager);
733   }
734   if (m_Library)
735   {
736     FT_Done_FreeType(m_Library);
737   }
738
739   m_Manager               = NULL;
740   m_Library               = NULL;
741
742   if (!m_txtCache)
743   {
744     CLog::Log(LOGNOTICE, "%s: called without cache", __FUNCTION__);
745   }
746   else
747   {
748     m_txtCache->PageUpdate = true;
749     CLog::Log(LOGDEBUG, "Teletext: Rendering ended");
750   }
751   return;
752 }
753
754 void CTeletextDecoder::PageInput(int Number)
755 {
756   m_updateTexture = true;
757
758   /* clear m_TempPage */
759   if (m_RenderInfo.InputCounter == 2)
760     m_TempPage = 0;
761
762   /* check for 0 & 9 on first position */
763   if (Number == 0 && m_RenderInfo.InputCounter == 2)
764   {
765     /* set page */
766     m_TempPage = m_LastPage; /* 0 toggles to last page as in program switching */
767     m_RenderInfo.InputCounter = -1;
768   }
769   else if (Number == 9 && m_RenderInfo.InputCounter == 2)
770   {
771     return;
772   }
773
774   /* show pageinput */
775   if (m_RenderInfo.ZoomMode == 2)
776   {
777     m_RenderInfo.ZoomMode = 1;
778     CopyBB2FB();
779   }
780
781   m_RenderInfo.PosY = 0;
782
783   switch (m_RenderInfo.InputCounter)
784   {
785   case 2:
786     SetPosX(1);
787     RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
788     RenderCharFB('-',          &Text_AtrTable[ATR_WB]);
789     RenderCharFB('-',          &Text_AtrTable[ATR_WB]);
790     break;
791
792   case 1:
793     SetPosX(2);
794     RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
795     break;
796
797   case 0:
798     SetPosX(3);
799     RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
800     break;
801   }
802
803   /* generate pagenumber */
804   m_TempPage |= Number << (m_RenderInfo.InputCounter*4);
805
806   m_RenderInfo.InputCounter--;
807
808   if (m_RenderInfo.InputCounter < 0)
809   {
810     /* disable SubPage zapping */
811     m_txtCache->ZapSubpageManual = false;
812
813     /* reset input */
814     m_RenderInfo.InputCounter = 2;
815
816     /* set new page */
817     m_LastPage = m_txtCache->Page;
818
819     m_txtCache->Page      = m_TempPage;
820     m_RenderInfo.HintMode = false;
821
822     /* check cache */
823     int subp = m_txtCache->SubPageTable[m_txtCache->Page];
824     if (subp != 0xFF)
825     {
826       m_txtCache->SubPage     = subp;
827       m_txtCache->PageUpdate  = true;
828     }
829     else
830     {
831       m_txtCache->SubPage = 0;
832 //      RenderMessage(PageNotFound);
833     }
834   }
835 }
836
837 void CTeletextDecoder::GetNextPageOne(bool up)
838 {
839   /* disable subpage zapping */
840   m_txtCache->ZapSubpageManual = false;
841
842   /* abort pageinput */
843   m_RenderInfo.InputCounter = 2;
844
845   /* find next cached page */
846   m_LastPage = m_txtCache->Page;
847
848   int subp;
849   do {
850     if (up)
851       CDVDTeletextTools::NextDec(&m_txtCache->Page);
852     else
853       CDVDTeletextTools::PrevDec(&m_txtCache->Page);
854     subp = m_txtCache->SubPageTable[m_txtCache->Page];
855   } while (subp == 0xFF && m_txtCache->Page != m_LastPage);
856
857   /* update Page */
858   if (m_txtCache->Page != m_LastPage)
859   {
860     if (m_RenderInfo.ZoomMode == 2)
861       m_RenderInfo.ZoomMode = 1;
862
863     m_txtCache->SubPage     = subp;
864     m_RenderInfo.HintMode   = false;
865     m_txtCache->PageUpdate  = true;
866   }
867 }
868
869 void CTeletextDecoder::GetNextSubPage(int offset)
870 {
871   /* abort pageinput */
872   m_RenderInfo.InputCounter = 2;
873
874   for (int loop = m_txtCache->SubPage + offset; loop != m_txtCache->SubPage; loop += offset)
875   {
876     if (loop < 0)
877       loop = 0x79;
878     else if (loop > 0x79)
879       loop = 0;
880     if (loop == m_txtCache->SubPage)
881       break;
882
883     if (m_txtCache->astCachetable[m_txtCache->Page][loop])
884     {
885       /* enable manual SubPage zapping */
886       m_txtCache->ZapSubpageManual = true;
887
888       /* update page */
889       if (m_RenderInfo.ZoomMode == 2) /* if zoomed to lower half */
890         m_RenderInfo.ZoomMode = 1; /* activate upper half */
891
892       m_txtCache->SubPage     = loop;
893       m_RenderInfo.HintMode   = false;
894       m_txtCache->PageUpdate  = true;
895
896       return;
897     }
898   }
899 }
900
901 void CTeletextDecoder::SwitchZoomMode()
902 {
903   if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
904   {
905     /* toggle mode */
906     m_RenderInfo.ZoomMode++;
907
908     if (m_RenderInfo.ZoomMode == 3)
909       m_RenderInfo.ZoomMode = 0;
910
911     /* update page */
912     m_txtCache->PageUpdate = true;
913   }
914 }
915
916 void CTeletextDecoder::SwitchTranspMode()
917 {
918   /* toggle mode */
919   if (!m_RenderInfo.TranspMode)
920     m_RenderInfo.TranspMode = true;
921   else
922     m_RenderInfo.TranspMode = false; /* backward to immediately switch to TV-screen */
923
924   /* set mode */
925   if (!m_RenderInfo.TranspMode) /* normal text-only */
926   {
927     ClearBB(m_txtCache->FullScrColor);
928     m_txtCache->PageUpdate = true;
929   }
930   else /* semi-transparent BG with FG text */
931   {
932     ClearBB(TXT_ColorTransp);
933     m_txtCache->PageUpdate = true;
934   }
935 }
936
937 void CTeletextDecoder::SwitchHintMode()
938 {
939   /* toggle mode */
940   m_RenderInfo.HintMode ^= true;
941
942   if (!m_RenderInfo.HintMode)  /* toggle evaluation of level 2.5 information by explicitly switching off HintMode */
943   {
944     m_RenderInfo.Showl25 ^= true;
945   }
946   /* update page */
947   m_txtCache->PageUpdate = true;
948 }
949
950 void CTeletextDecoder::ColorKey(int target)
951 {
952   if (!target)
953     return;
954
955   if (m_RenderInfo.ZoomMode == 2)
956     m_RenderInfo.ZoomMode = 1;
957
958   m_LastPage                = m_txtCache->Page;
959   m_txtCache->Page          = target;
960   m_txtCache->SubPage       = m_txtCache->SubPageTable[m_txtCache->Page];
961   m_RenderInfo.InputCounter = 2;
962   m_RenderInfo.HintMode     = false;
963   m_txtCache->PageUpdate    = true;
964 }
965
966 void CTeletextDecoder::StartPageCatching()
967 {
968   m_RenderInfo.PageCatching = true;
969
970   /* abort pageinput */
971   m_RenderInfo.InputCounter = 2;
972
973   /* show info line */
974   m_RenderInfo.ZoomMode = 0;
975   m_RenderInfo.PosX     = 0;
976   m_RenderInfo.PosY     = 24*m_RenderInfo.FontHeight;
977
978   /* check for pagenumber(s) */
979   m_CatchRow            = 1;
980   m_CatchCol            = 0;
981   m_CatchedPage         = 0;
982   m_PCOldRow            = 0;
983   m_PCOldCol            = 0; /* no inverted page number to restore yet */
984   CatchNextPage(0, 1);
985
986   if (!m_CatchedPage)
987   {
988     m_RenderInfo.PageCatching = false;
989     m_txtCache->PageUpdate    = true;
990     return;
991   }
992 }
993
994 void CTeletextDecoder::StopPageCatching()
995 {
996   /* set new page */
997   if (m_RenderInfo.ZoomMode == 2)
998     m_RenderInfo.ZoomMode = 1;
999
1000   m_LastPage                = m_txtCache->Page;
1001   m_txtCache->Page          = m_CatchedPage;
1002   m_RenderInfo.HintMode     = false;
1003   m_txtCache->PageUpdate    = true;
1004   m_RenderInfo.PageCatching = false;
1005
1006   int subp = m_txtCache->SubPageTable[m_txtCache->Page];
1007   if (subp != 0xFF)
1008     m_txtCache->SubPage = subp;
1009   else
1010     m_txtCache->SubPage = 0;
1011 }
1012
1013 void CTeletextDecoder::CatchNextPage(int firstlineinc, int inc)
1014 {
1015   int tmp_page, allowwrap = 1; /* allow first wrap around */
1016
1017   /* catch next page */
1018   for(;;)
1019   {
1020     unsigned char *p = &(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol]);
1021     TextPageAttr_t a = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol];
1022
1023     if (!(a.charset == C_G1C || a.charset == C_G1S) && /* no mosaic */
1024        (a.fg != a.bg) && /* not hidden */
1025        (*p >= '1' && *p <= '8' && /* valid page number */
1026         *(p+1) >= '0' && *(p+1) <= '9' &&
1027         *(p+2) >= '0' && *(p+2) <= '9') &&
1028        (m_CatchRow == 0 || (*(p-1) < '0' || *(p-1) > '9')) && /* non-numeric char before and behind */
1029        (m_CatchRow == 37 || (*(p+3) < '0' || *(p+3) > '9')))
1030     {
1031       tmp_page = ((*p - '0')<<8) | ((*(p+1) - '0')<<4) | (*(p+2) - '0');
1032
1033 #if 0
1034       if (tmp_page != m_CatchedPage)  /* confusing to skip identical page numbers - I want to reach what I aim to */
1035 #endif
1036       {
1037         m_CatchedPage = tmp_page;
1038         RenderCatchedPage();
1039         m_CatchCol += inc;  /* FIXME: limit */
1040         return;
1041       }
1042     }
1043
1044     if (firstlineinc > 0)
1045     {
1046       m_CatchRow++;
1047       m_CatchCol = 0;
1048       firstlineinc = 0;
1049     }
1050     else if (firstlineinc < 0)
1051     {
1052       m_CatchRow--;
1053       m_CatchCol = 37;
1054       firstlineinc = 0;
1055     }
1056     else
1057       m_CatchCol += inc;
1058
1059     if (m_CatchCol > 37)
1060     {
1061       m_CatchRow++;
1062       m_CatchCol = 0;
1063     }
1064     else if (m_CatchCol < 0)
1065     {
1066       m_CatchRow--;
1067       m_CatchCol = 37;
1068     }
1069
1070     if (m_CatchRow > 23)
1071     {
1072       if (allowwrap)
1073       {
1074         allowwrap = 0;
1075         m_CatchRow = 1;
1076         m_CatchCol = 0;
1077       }
1078       else
1079       {
1080         return;
1081       }
1082     }
1083     else if (m_CatchRow < 1)
1084     {
1085       if (allowwrap)
1086       {
1087         allowwrap = 0;
1088         m_CatchRow = 23;
1089         m_CatchCol =37;
1090       }
1091       else
1092       {
1093         return;
1094       }
1095     }
1096   }
1097 }
1098
1099 void CTeletextDecoder::RenderCatchedPage()
1100 {
1101   int zoom = 0;
1102   m_updateTexture = true;
1103
1104   /* handle zoom */
1105   if (m_RenderInfo.ZoomMode)
1106     zoom = 1<<10;
1107
1108   if (m_PCOldRow || m_PCOldCol) /* not at first call */
1109   {
1110     /* restore pagenumber */
1111     SetPosX(m_PCOldCol);
1112
1113     if (m_RenderInfo.ZoomMode == 2)
1114       m_RenderInfo.PosY = (m_PCOldRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1);
1115     else
1116       m_RenderInfo.PosY = m_PCOldRow*m_RenderInfo.FontHeight*((zoom>>10)+1);
1117
1118     RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol    ], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol    ]);
1119     RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 1], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 1]);
1120     RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 2], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 2]);
1121   }
1122
1123   m_PCOldRow = m_CatchRow;
1124   m_PCOldCol = m_CatchCol;
1125
1126   /* mark pagenumber */
1127   if (m_RenderInfo.ZoomMode == 1 && m_CatchRow > 11)
1128   {
1129     m_RenderInfo.ZoomMode = 2;
1130     CopyBB2FB();
1131   }
1132   else if (m_RenderInfo.ZoomMode == 2 && m_CatchRow < 12)
1133   {
1134     m_RenderInfo.ZoomMode = 1;
1135     CopyBB2FB();
1136   }
1137   SetPosX(m_CatchCol);
1138
1139   if (m_RenderInfo.ZoomMode == 2)
1140     m_RenderInfo.PosY = (m_CatchRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1);
1141   else
1142     m_RenderInfo.PosY = m_CatchRow*m_RenderInfo.FontHeight*((zoom>>10)+1);
1143
1144   TextPageAttr_t a0 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol    ];
1145   TextPageAttr_t a1 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 1];
1146   TextPageAttr_t a2 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 2];
1147   int t;
1148
1149   /* exchange colors */
1150   t = a0.fg; a0.fg = a0.bg; a0.bg = t;
1151   t = a1.fg; a1.fg = a1.bg; a1.bg = t;
1152   t = a2.fg; a2.fg = a2.bg; a2.bg = t;
1153
1154   RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol    ], &a0);
1155   RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 1], &a1);
1156   RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 2], &a2);
1157 }
1158
1159 void CTeletextDecoder::RenderPage()
1160 {
1161   int StartRow = 0;
1162   int national_subset_bak = m_txtCache->NationalSubset;
1163
1164   if (m_txtCache->PageUpdate)
1165     m_updateTexture = true;
1166
1167   /* update page or timestring */
1168   if (m_txtCache->PageUpdate && m_txtCache->PageReceiving != m_txtCache->Page && m_RenderInfo.InputCounter == 2)
1169   {
1170     /* reset update flag */
1171     m_txtCache->PageUpdate = false;
1172     if (m_RenderInfo.Boxed && m_RenderInfo.SubtitleDelay)
1173     {
1174       TextSubtitleCache_t* c = NULL;
1175       int j = -1;
1176       for (int i = 0; i < SUBTITLE_CACHESIZE; i++)
1177       {
1178         if (j == -1 && !m_RenderInfo.SubtitleCache[i])
1179           j = i;
1180         if (m_RenderInfo.SubtitleCache[i] && !m_RenderInfo.SubtitleCache[i]->Valid)
1181         {
1182           c = m_RenderInfo.SubtitleCache[i];
1183           break;
1184         }
1185       }
1186       if (c == NULL)
1187       {
1188         if (j == -1) // no more space in SubtitleCache
1189           return;
1190
1191         c = new TextSubtitleCache_t;
1192         if (c == NULL)
1193           return;
1194
1195         memset(c, 0x00, sizeof(TextSubtitleCache_t));
1196         m_RenderInfo.SubtitleCache[j] = c;
1197       }
1198       c->Valid = true;
1199       c->Timestamp = XbmcThreads::SystemClockMillis()/1000;
1200
1201       if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
1202       {
1203         TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, c->PageChar, c->PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof);
1204         if (p)
1205         {
1206           m_RenderInfo.Boxed = p->boxed;
1207         }
1208       }
1209       m_RenderInfo.DelayStarted = true;
1210       return;
1211     }
1212     m_RenderInfo.DelayStarted = false;
1213     /* decode page */
1214     if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
1215     {
1216       TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, m_RenderInfo.PageChar, m_RenderInfo.PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof);
1217       if (p)
1218       {
1219         m_RenderInfo.PageInfo = p;
1220         m_RenderInfo.Boxed = p->boxed;
1221       }
1222       if (m_RenderInfo.Boxed || m_RenderInfo.TranspMode)
1223         FillBorder(GetColorRGB(TXT_ColorTransp));
1224       else
1225         FillBorder(GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor));
1226
1227       if (m_txtCache->ColorTable)                   /* as late as possible to shorten the time the old page is displayed with the new colors */
1228         SetColors(m_txtCache->ColorTable, 16, 16);  /* set colors for CLUTs 2+3 */
1229     }
1230     else
1231       StartRow = 1;
1232
1233     DoRenderPage(StartRow, national_subset_bak);
1234   }
1235   else
1236   {
1237     if (m_RenderInfo.DelayStarted)
1238     {
1239       long now = XbmcThreads::SystemClockMillis()/1000;
1240       for (int i = 0; i < SUBTITLE_CACHESIZE ; i++)
1241       {
1242         if (m_RenderInfo.SubtitleCache[i] && m_RenderInfo.SubtitleCache[i]->Valid && now - m_RenderInfo.SubtitleCache[i]->Timestamp >= (long)m_RenderInfo.SubtitleDelay)
1243         {
1244           memcpy(m_RenderInfo.PageChar, m_RenderInfo.SubtitleCache[i]->PageChar, 40 * 25);
1245           memcpy(m_RenderInfo.PageAtrb, m_RenderInfo.SubtitleCache[i]->PageAtrb, 40 * 25 * sizeof(TextPageAttr_t));
1246           DoRenderPage(StartRow, national_subset_bak);
1247           m_RenderInfo.SubtitleCache[i]->Valid = false;
1248           return;
1249         }
1250       }
1251     }
1252     if (m_RenderInfo.ZoomMode != 2)
1253     {
1254       m_RenderInfo.PosY = 0;
1255       if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff)
1256       {
1257         m_RenderInfo.PageAtrb[32].fg = TXT_ColorYellow;
1258         m_RenderInfo.PageAtrb[32].bg = TXT_ColorMenu1;
1259         int showpage    = m_txtCache->PageReceiving;
1260         int showsubpage = m_txtCache->SubPageTable[showpage];
1261         if (showsubpage!=0xff)
1262         {
1263           TextCachedPage_t *pCachedPage;
1264           pCachedPage = m_txtCache->astCachetable[showpage][showsubpage];
1265           if (pCachedPage && IsDec(showpage))
1266           {
1267             m_RenderInfo.PosX = 0;
1268             if (m_RenderInfo.InputCounter == 2)
1269             {
1270               if (m_txtCache->BTTok && !m_txtCache->BasicTop[m_txtCache->Page]) /* page non-existent according to TOP (continue search anyway) */
1271               {
1272                 m_RenderInfo.PageAtrb[0].fg = TXT_ColorWhite;
1273                 m_RenderInfo.PageAtrb[0].bg = TXT_ColorRed;
1274               }
1275               else
1276               {
1277                 m_RenderInfo.PageAtrb[0].fg = TXT_ColorYellow;
1278                 m_RenderInfo.PageAtrb[0].bg = TXT_ColorMenu1;
1279               }
1280               CDVDTeletextTools::Hex2Str((char*)m_RenderInfo.PageChar+3, m_txtCache->Page);
1281
1282               int col;
1283               for (col = m_RenderInfo.nofirst; col < 7; col++) // selected page
1284               {
1285                 RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[0]);
1286               }
1287               RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[32]);
1288             }
1289             else
1290               SetPosX(8);
1291
1292             memcpy(&m_RenderInfo.PageChar[8], pCachedPage->p0, 24); /* header line without timestring */
1293             for (int i = 0; i < 24; i++)
1294             {
1295               RenderCharFB(pCachedPage->p0[i], &m_RenderInfo.PageAtrb[32]);
1296             }
1297
1298             /* Update on every Header number change */
1299             if (pCachedPage->p0[2] != prevHeaderPage)
1300             {
1301               prevHeaderPage = pCachedPage->p0[2];
1302               m_updateTexture = true;
1303             }
1304           }
1305         }
1306       }
1307
1308       /* update timestring */
1309       SetPosX(32);
1310       for (int i = 0; i < 8; i++)
1311       {
1312         if (!m_RenderInfo.PageAtrb[32+i].flashing)
1313           RenderCharFB(m_txtCache->TimeString[i], &m_RenderInfo.PageAtrb[32]);
1314         else
1315         {
1316           SetPosX(33+i);
1317           m_RenderInfo.PageChar[32+i] = m_RenderInfo.PageChar[32+i];
1318         }
1319       }
1320
1321       /* Update on every changed second */
1322       if (m_txtCache->TimeString[7] != prevTimeSec)
1323       {
1324         prevTimeSec = m_txtCache->TimeString[7];
1325         m_updateTexture = true;
1326       }
1327     }
1328     DoFlashing(StartRow);
1329     m_txtCache->NationalSubset = national_subset_bak;
1330   }
1331 }
1332
1333 void CTeletextDecoder::DoFlashing(int startrow)
1334 {
1335   /* get national subset */
1336   if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */
1337      m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */
1338   {
1339     m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national];
1340   }
1341
1342   /* Flashing */
1343   TextPageAttr_t flashattr;
1344   char flashchar;
1345   long flashphase = XbmcThreads::SystemClockMillis() % 1000;
1346
1347   int srow = startrow;
1348   int erow = 24;
1349   int factor=1;
1350
1351   switch (m_RenderInfo.ZoomMode)
1352   {
1353     case 1: erow = 12; factor=2;break;
1354     case 2: srow = 12; factor=2;break;
1355   }
1356
1357   m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight*factor;
1358   for (int row = srow; row < erow; row++)
1359   {
1360     int index = row * 40;
1361     int dhset = 0;
1362     int incflash = 3;
1363     int decflash = 2;
1364
1365     m_RenderInfo.PosX = 0;
1366     for (int col = m_RenderInfo.nofirst; col < 40; col++)
1367     {
1368       if (m_RenderInfo.PageAtrb[index + col].flashing && m_RenderInfo.PageChar[index + col] > 0x20 && m_RenderInfo.PageChar[index + col] != 0xff )
1369       {
1370         SetPosX(col);
1371         flashchar = m_RenderInfo.PageChar[index + col];
1372         bool doflash = false;
1373         memcpy(&flashattr, &m_RenderInfo.PageAtrb[index + col], sizeof(TextPageAttr_t));
1374         switch (flashattr.flashing &0x1c) // Flash Rate
1375         {
1376           case 0x00 :  // 1 Hz
1377             if (flashphase>500) doflash = true;
1378             break;
1379           case 0x04 :  // 2 Hz  Phase 1
1380             if (flashphase<250) doflash = true;
1381             break;
1382           case 0x08 :  // 2 Hz  Phase 2
1383             if (flashphase>=250 && flashphase<500) doflash = true;
1384             break;
1385           case 0x0c :  // 2 Hz  Phase 3
1386             if (flashphase>=500 && flashphase<750) doflash = true;
1387             break;
1388           case 0x10 :  // incremental flash
1389             incflash++;
1390             if (incflash>3) incflash = 1;
1391             switch (incflash)
1392             {
1393               case 1: if (flashphase<250) doflash = true; break;
1394               case 2: if (flashphase>=250 && flashphase<500) doflash = true;break;
1395               case 3: if (flashphase>=500 && flashphase<750) doflash = true;
1396             }
1397             break;
1398           case 0x14 :  // decremental flash
1399             decflash--;
1400             if (decflash<1) decflash = 3;
1401             switch (decflash)
1402             {
1403               case 1: if (flashphase<250) doflash = true; break;
1404               case 2: if (flashphase>=250 && flashphase<500) doflash = true;break;
1405               case 3: if (flashphase>=500 && flashphase<750) doflash = true;
1406             }
1407             break;
1408
1409         }
1410
1411         switch (flashattr.flashing &0x03) // Flash Mode
1412         {
1413           case 0x01 :  // normal Flashing
1414             if (doflash) flashattr.fg = flashattr.bg;
1415             break;
1416           case 0x02 :  // inverted Flashing
1417             doflash = !doflash;
1418             if (doflash) flashattr.fg = flashattr.bg;
1419             break;
1420           case 0x03 :  // color Flashing
1421             if (doflash) flashattr.fg = flashattr.fg + (flashattr.fg > 7 ? (-8) : 8);
1422             break;
1423
1424         }
1425         RenderCharFB(flashchar, &flashattr);
1426         if (flashattr.doublew) col++;
1427         if (flashattr.doubleh) dhset = 1;
1428
1429         m_updateTexture = true;
1430       }
1431     }
1432     if (dhset)
1433     {
1434       row++;
1435       m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor;
1436     }
1437     m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor;
1438   }
1439 }
1440
1441 void CTeletextDecoder::DoRenderPage(int startrow, int national_subset_bak)
1442 {
1443   /* display first column?  */
1444   m_RenderInfo.nofirst = m_RenderInfo.Show39;
1445   for (int row = 1; row < 24; row++)
1446   {
1447     int Byte = m_RenderInfo.PageChar[row*40];
1448     if (Byte != ' '  && Byte != 0x00 && Byte != 0xFF && m_RenderInfo.PageAtrb[row*40].fg != m_RenderInfo.PageAtrb[row*40].bg)
1449     {
1450       m_RenderInfo.nofirst = 0;
1451       break;
1452     }
1453   }
1454
1455   if (m_RenderInfo.TranspMode || m_RenderInfo.Boxed)
1456   {
1457     FillBorder(GetColorRGB(TXT_ColorTransp));//ClearBB(transp);
1458     m_RenderInfo.ClearBBColor = TXT_ColorTransp;
1459   }
1460
1461   /* get national subset */
1462   if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */
1463     m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */
1464   {
1465     m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national];
1466   }
1467   /* render page */
1468   if (m_RenderInfo.PageInfo && (m_RenderInfo.PageInfo->function == FUNC_GDRCS || m_RenderInfo.PageInfo->function == FUNC_DRCS)) /* character definitions */
1469   {
1470     #define DRCSROWS 8
1471     #define DRCSCOLS (48/DRCSROWS)
1472     #define DRCSZOOMX 3
1473     #define DRCSZOOMY 5
1474     #define DRCSXSPC (12*DRCSZOOMX + 2)
1475     #define DRCSYSPC (10*DRCSZOOMY + 2)
1476
1477     unsigned char ax[] = { /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */
1478       DRCSZOOMX * 0,
1479       DRCSZOOMX * 1,
1480       DRCSZOOMX * 2,
1481       DRCSZOOMX * 3,
1482       DRCSZOOMX * 4,
1483       DRCSZOOMX * 5,
1484       DRCSZOOMX * 6,
1485       DRCSZOOMX * 7,
1486       DRCSZOOMX * 8,
1487       DRCSZOOMX * 9,
1488       DRCSZOOMX * 10,
1489       DRCSZOOMX * 11,
1490       DRCSZOOMX * 12,
1491       DRCSZOOMY * 0,
1492       DRCSZOOMY * 1,
1493       DRCSZOOMY * 2,
1494       DRCSZOOMY * 3,
1495       DRCSZOOMY * 4,
1496       DRCSZOOMY * 5,
1497       DRCSZOOMY * 6,
1498       DRCSZOOMY * 7,
1499       DRCSZOOMY * 8,
1500       DRCSZOOMY * 9,
1501       DRCSZOOMY * 10
1502     };
1503
1504     ClearBB(TXT_ColorBlack);
1505     for (int col = 0; col < 24*40; col++)
1506       m_RenderInfo.PageAtrb[col] = Text_AtrTable[ATR_WB];
1507
1508     for (int row = 0; row < DRCSROWS; row++)
1509     {
1510       for (int col = 0; col < DRCSCOLS; col++)
1511       {
1512         RenderDRCS(m_RenderInfo.Width,
1513           m_RenderInfo.PageChar + 20 * (DRCSCOLS * row + col + 2),
1514           m_TextureBuffer
1515           + (m_RenderInfo.FontHeight + DRCSYSPC * row + m_RenderInfo.Height) * m_RenderInfo.Width
1516           + DRCSXSPC * col,
1517           ax, GetColorRGB(TXT_ColorWhite), GetColorRGB(TXT_ColorBlack));
1518       }
1519     }
1520     memset(m_RenderInfo.PageChar + 40, 0xff, 24*40); /* don't render any char below row 0 */
1521   }
1522   m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight;
1523   for (int row = startrow; row < 24; row++)
1524   {
1525     int index = row * 40;
1526
1527     m_RenderInfo.PosX = 0;
1528     for (int col = m_RenderInfo.nofirst; col < 40; col++)
1529     {
1530       RenderCharBB(m_RenderInfo.PageChar[index + col], &m_RenderInfo.PageAtrb[index + col]);
1531
1532       if (m_RenderInfo.PageAtrb[index + col].doubleh && m_RenderInfo.PageChar[index + col] != 0xff)  /* disable lower char in case of doubleh setting in l25 objects */
1533         m_RenderInfo.PageChar[index + col + 40] = 0xff;
1534       if (m_RenderInfo.PageAtrb[index + col].doublew)  /* skip next column if double width */
1535       {
1536         col++;
1537         if (m_RenderInfo.PageAtrb[index + col-1].doubleh && m_RenderInfo.PageChar[index + col] != 0xff)  /* disable lower char in case of doubleh setting in l25 objects */
1538           m_RenderInfo.PageChar[index + col + 40] = 0xff;
1539       }
1540     }
1541     m_RenderInfo.PosY += m_RenderInfo.FontHeight;
1542   }
1543   DoFlashing(startrow);
1544
1545   /* update framebuffer */
1546   CopyBB2FB();
1547   m_txtCache->NationalSubset = national_subset_bak;
1548 }
1549
1550 void CTeletextDecoder::Decode_BTT()
1551 {
1552   /* basic top table */
1553   int current, b1, b2, b3, b4;
1554   unsigned char btt[23*40];
1555
1556   if (m_txtCache->SubPageTable[0x1f0] == 0xff || 0 == m_txtCache->astCachetable[0x1f0][m_txtCache->SubPageTable[0x1f0]]) /* not yet received */
1557     return;
1558
1559   g_application.m_pPlayer->LoadPage(0x1f0, m_txtCache->SubPageTable[0x1f0],btt);
1560   if (btt[799] == ' ') /* not completely received or error */
1561     return;
1562
1563   current = 0x100;
1564   for (int i = 0; i < 800; i++)
1565   {
1566     b1 = btt[i];
1567     if (b1 == ' ')
1568       b1 = 0;
1569     else
1570     {
1571       b1 = dehamming[b1];
1572       if (b1 == 0xFF) /* hamming error in btt */
1573       {
1574         btt[799] = ' '; /* mark btt as not received */
1575         return;
1576       }
1577     }
1578     m_txtCache->BasicTop[current] = b1;
1579     CDVDTeletextTools::NextDec(&current);
1580   }
1581   /* page linking table */
1582   m_txtCache->ADIP_PgMax = -1; /* rebuild table of adip pages */
1583   for (int i = 0; i < 10; i++)
1584   {
1585     b1 = dehamming[btt[800 + 8*i +0]];
1586
1587     if (b1 == 0xE)
1588       continue; /* unused */
1589     else if (b1 == 0xF)
1590       break; /* end */
1591
1592     b4 = dehamming[btt[800 + 8*i +7]];
1593
1594     if (b4 != 2) /* only adip, ignore multipage (1) */
1595       continue;
1596
1597     b2 = dehamming[btt[800 + 8*i +1]];
1598     b3 = dehamming[btt[800 + 8*i +2]];
1599
1600     if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF)
1601     {
1602       CLog::Log(LOGERROR, "CTeletextDecoder::Decode_BTT <Biterror in btt/plt index %d>", i);
1603       btt[799] = ' '; /* mark btt as not received */
1604       return;
1605     }
1606
1607     b1 = b1<<8 | b2<<4 | b3; /* page number */
1608     m_txtCache->ADIP_Pg[++m_txtCache->ADIP_PgMax] = b1;
1609   }
1610
1611   m_txtCache->BTTok = true;
1612 }
1613
1614 void CTeletextDecoder::Decode_ADIP() /* additional information table */
1615 {
1616   int i, p, j, b1, b2, b3, charfound;
1617   unsigned char padip[23*40];
1618
1619   for (i = 0; i <= m_txtCache->ADIP_PgMax; i++)
1620   {
1621     p = m_txtCache->ADIP_Pg[i];
1622     if (!p || m_txtCache->SubPageTable[p] == 0xff || 0 == m_txtCache->astCachetable[p][m_txtCache->SubPageTable[p]]) /* not cached (avoid segfault) */
1623       continue;
1624
1625     g_application.m_pPlayer->LoadPage(p,m_txtCache->SubPageTable[p],padip);
1626     for (j = 0; j < 44; j++)
1627     {
1628       b1 = dehamming[padip[20*j+0]];
1629       if (b1 == 0xE)
1630         continue; /* unused */
1631
1632       if (b1 == 0xF)
1633         break; /* end */
1634
1635       b2 = dehamming[padip[20*j+1]];
1636       b3 = dehamming[padip[20*j+2]];
1637
1638       if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF)
1639       {
1640         CLog::Log(LOGERROR, "CTeletextDecoder::Decode_BTT <Biterror in ait %03x %d %02x %02x %02x %02x %02x %02x>", p, j,
1641                    padip[20*j+0],
1642                    padip[20*j+1],
1643                    padip[20*j+2],
1644                    b1, b2, b3
1645                    );
1646         return;
1647       }
1648
1649       if (b1>8 || b2>9 || b3>9) /* ignore extries with invalid or hex page numbers */
1650       {
1651         continue;
1652       }
1653
1654       b1 = b1<<8 | b2<<4 | b3; /* page number */
1655       charfound = 0; /* flag: no printable char found */
1656
1657       for (b2 = 11; b2 >= 0; b2--)
1658       {
1659         b3 = deparity[padip[20*j + 8 + b2]];
1660         if (b3 < ' ')
1661           b3 = ' ';
1662
1663         if (b3 == ' ' && !charfound)
1664           m_txtCache->ADIPTable[b1][b2] = '\0';
1665         else
1666         {
1667           m_txtCache->ADIPTable[b1][b2] = b3;
1668           charfound = 1;
1669         }
1670       }
1671     } /* next link j */
1672
1673     m_txtCache->ADIP_Pg[i] = 0; /* completely decoded: clear entry */
1674   } /* next adip page i */
1675
1676   while ((m_txtCache->ADIP_PgMax >= 0) && !m_txtCache->ADIP_Pg[m_txtCache->ADIP_PgMax]) /* and shrink table */
1677     m_txtCache->ADIP_PgMax--;
1678 }
1679
1680 int CTeletextDecoder::TopText_GetNext(int startpage, int up, int findgroup)
1681 {
1682   int current, nextgrp, nextblk;
1683
1684   int stoppage =  (IsDec(startpage) ? startpage : startpage & 0xF00); // avoid endless loop in hexmode
1685   nextgrp = nextblk = 0;
1686   current = startpage;
1687
1688   do {
1689     if (up)
1690       CDVDTeletextTools::NextDec(&current);
1691     else
1692       CDVDTeletextTools::PrevDec(&current);
1693
1694     if (!m_txtCache->BTTok || m_txtCache->BasicTop[current]) /* only if existent */
1695     {
1696       if (findgroup)
1697       {
1698         if (m_txtCache->BasicTop[current] >= 6 && m_txtCache->BasicTop[current] <= 7)
1699           return current;
1700         if (!nextgrp && (current&0x00F) == 0)
1701           nextgrp = current;
1702       }
1703       if (m_txtCache->BasicTop[current] >= 2 && m_txtCache->BasicTop[current] <= 5) /* always find block */
1704         return current;
1705
1706       if (!nextblk && (current&0x0FF) == 0)
1707         nextblk = current;
1708     }
1709   } while (current != stoppage);
1710
1711   if (nextgrp)
1712     return nextgrp;
1713   else if (nextblk)
1714     return nextblk;
1715   else
1716     return current;
1717 }
1718
1719 void CTeletextDecoder::Showlink(int column, int linkpage)
1720 {
1721   unsigned char line[] = "   >???   ";
1722   int oldfontwidth = m_RenderInfo.FontWidth;
1723   int yoffset;
1724
1725   if (m_YOffset)
1726     yoffset = 0;
1727   else
1728     yoffset = m_RenderInfo.Height;
1729
1730   int abx = ((m_RenderInfo.Width)%(40-m_RenderInfo.nofirst) == 0 ? m_RenderInfo.Width+1 : (m_RenderInfo.Width)/(((m_RenderInfo.Width)%(40-m_RenderInfo.nofirst)))+1);// distance between 'inserted' pixels
1731   int width = m_RenderInfo.Width /4;
1732
1733   m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight;
1734
1735   if (m_RenderInfo.Boxed)
1736   {
1737     m_RenderInfo.PosX = column*width;
1738     FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, m_RenderInfo.Width, m_RenderInfo.FontHeight, GetColorRGB(TXT_ColorTransp));
1739     return;
1740   }
1741
1742   if (m_txtCache->ADIPTable[linkpage][0])
1743   {
1744     m_RenderInfo.PosX = column*width;
1745     int l = strlen(m_txtCache->ADIPTable[linkpage]);
1746
1747     if (l > 9) /* smaller font, if no space for one half space at front and end */
1748       SetFontWidth(oldfontwidth * 10 / (l+1));
1749
1750     FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, width+(m_RenderInfo.Width%4), m_RenderInfo.FontHeight, GetColorRGB((enumTeletextColor)Text_AtrTable[ATR_L250 + column].bg));
1751     m_RenderInfo.PosX += ((width) - (l*m_RenderInfo.FontWidth+l*m_RenderInfo.FontWidth/abx))/2; /* center */
1752
1753     for (char *p = m_txtCache->ADIPTable[linkpage]; *p; p++)
1754       RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]);
1755
1756     SetFontWidth(oldfontwidth);
1757   }
1758   else /* display number */
1759   {
1760     m_RenderInfo.PosX = column*width;
1761     FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, m_RenderInfo.Width-m_RenderInfo.PosX, m_RenderInfo.FontHeight, GetColorRGB((enumTeletextColor)Text_AtrTable[ATR_L250 + column].bg));
1762     if (linkpage < m_txtCache->Page)
1763     {
1764       line[6] = '<';
1765       CDVDTeletextTools::Hex2Str((char*)line + 5, linkpage);
1766     }
1767     else
1768       CDVDTeletextTools::Hex2Str((char*)line + 6, linkpage);
1769
1770     for (unsigned char *p = line; p < line+9; p++)
1771       RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]);
1772   }
1773 }
1774
1775 void CTeletextDecoder::CreateLine25()
1776 {
1777   /* btt completely received and not yet decoded */
1778   if (!m_txtCache->BTTok)
1779     Decode_BTT();
1780
1781   if (m_txtCache->ADIP_PgMax >= 0)
1782     Decode_ADIP();
1783
1784   if (!m_RenderInfo.ShowHex && m_RenderInfo.ShowFlof &&
1785      (m_txtCache->FlofPages[m_txtCache->Page][0] || m_txtCache->FlofPages[m_txtCache->Page][1] || m_txtCache->FlofPages[m_txtCache->Page][2] || m_txtCache->FlofPages[m_txtCache->Page][3])) // FLOF-Navigation present
1786   {
1787     m_RenderInfo.Prev_100 = m_txtCache->FlofPages[m_txtCache->Page][0];
1788     m_RenderInfo.Prev_10  = m_txtCache->FlofPages[m_txtCache->Page][1];
1789     m_RenderInfo.Next_10  = m_txtCache->FlofPages[m_txtCache->Page][2];
1790     m_RenderInfo.Next_100 = m_txtCache->FlofPages[m_txtCache->Page][3];
1791
1792     m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight;
1793     m_RenderInfo.PosX = 0;
1794     for (int i=m_RenderInfo.nofirst; i<40; i++)
1795       RenderCharBB(m_RenderInfo.PageChar[24*40 + i], &m_RenderInfo.PageAtrb[24*40 + i]);
1796   }
1797   else
1798   {
1799     /*  normal: blk-1, grp+1, grp+2, blk+1 */
1800     /*  hex:    hex+1, blk-1, grp+1, blk+1 */
1801     if (m_RenderInfo.ShowHex)
1802     {
1803       /* arguments: startpage, up, findgroup */
1804       m_RenderInfo.Prev_100 = NextHex(m_txtCache->Page);
1805       m_RenderInfo.Prev_10  = TopText_GetNext(m_txtCache->Page, 0, 0);
1806       m_RenderInfo.Next_10  = TopText_GetNext(m_txtCache->Page, 1, 1);
1807     }
1808     else
1809     {
1810       m_RenderInfo.Prev_100 = TopText_GetNext(m_txtCache->Page, 0, 0);
1811       m_RenderInfo.Prev_10  = TopText_GetNext(m_txtCache->Page, 1, 1);
1812       m_RenderInfo.Next_10  = TopText_GetNext(m_RenderInfo.Prev_10, 1, 1);
1813     }
1814     m_RenderInfo.Next_100 = TopText_GetNext(m_RenderInfo.Next_10, 1, 0);
1815     Showlink(0, m_RenderInfo.Prev_100);
1816     Showlink(1, m_RenderInfo.Prev_10);
1817     Showlink(2, m_RenderInfo.Next_10);
1818     Showlink(3, m_RenderInfo.Next_100);
1819   }
1820 }
1821
1822 void CTeletextDecoder::RenderCharFB(int Char, TextPageAttr_t *Attribute)
1823 {
1824   RenderCharIntern(&m_RenderInfo, Char, Attribute, m_RenderInfo.ZoomMode, m_YOffset);
1825 }
1826
1827 void CTeletextDecoder::RenderCharBB(int Char, TextPageAttr_t *Attribute)
1828 {
1829   RenderCharIntern(&m_RenderInfo, Char, Attribute, 0, m_RenderInfo.Height-m_YOffset);
1830 }
1831
1832 void CTeletextDecoder::CopyBB2FB()
1833 {
1834   color_t *src, *dst, *topsrc;
1835   int screenwidth;
1836   color_t fillcolor;
1837
1838   /* line 25 */
1839   if (!m_RenderInfo.PageCatching)
1840     CreateLine25();
1841
1842   /* copy backbuffer to framebuffer */
1843   if (!m_RenderInfo.ZoomMode)
1844   {
1845     if (m_YOffset)
1846       m_YOffset = 0;
1847     else
1848       m_YOffset = m_RenderInfo.Height;
1849
1850     if (m_RenderInfo.ClearBBColor >= 0)
1851     {
1852       m_RenderInfo.ClearBBColor = -1;
1853     }
1854     return;
1855   }
1856
1857   src = dst = topsrc = m_TextureBuffer + m_RenderInfo.Width;
1858
1859   if (m_YOffset)
1860   {
1861     dst += m_RenderInfo.Width * m_RenderInfo.Height;
1862   }
1863   else
1864   {
1865     src    += m_RenderInfo.Width * m_RenderInfo.Height;
1866     topsrc += m_RenderInfo.Width * m_RenderInfo.Height;
1867   }
1868
1869   if (!m_RenderInfo.PageCatching)
1870     SDL_memcpy4(dst+(24*m_RenderInfo.FontHeight)*m_RenderInfo.Width, src + (24*m_RenderInfo.FontHeight)*m_RenderInfo.Width, m_RenderInfo.Width*m_RenderInfo.FontHeight); /* copy line25 in normal height */
1871
1872   if (m_RenderInfo.TranspMode)
1873     fillcolor = GetColorRGB(TXT_ColorTransp);
1874   else
1875     fillcolor = GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor);
1876
1877   if (m_RenderInfo.ZoomMode == 2)
1878     src += 12*m_RenderInfo.FontHeight*m_RenderInfo.Width;
1879
1880   screenwidth = m_RenderInfo.Width;
1881
1882   for (int i = 12*m_RenderInfo.FontHeight; i; i--)
1883   {
1884     SDL_memcpy4(dst, src, screenwidth);
1885     dst += m_RenderInfo.Width;
1886     SDL_memcpy4(dst, src, screenwidth);
1887     dst += m_RenderInfo.Width;
1888     src += m_RenderInfo.Width;
1889   }
1890
1891   for (int i = m_RenderInfo.Height - 25*m_RenderInfo.FontHeight; i >= 0;i--)
1892   {
1893     SDL_memset4(dst + m_RenderInfo.Width*(m_RenderInfo.FontHeight+i), fillcolor, screenwidth);
1894   }
1895 }
1896
1897 FT_Error CTeletextDecoder::MyFaceRequester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *aface)
1898 {
1899   FT_Error result = FT_New_Face(library, (const char*)face_id, 0, aface);
1900
1901   if (!result)
1902     CLog::Log(LOGNOTICE, "Teletext font %s loaded", (char*)face_id);
1903   else
1904     CLog::Log(LOGERROR, "Opening of Teletext font %s failed", (char*)face_id);
1905
1906   return result;
1907 }
1908
1909 void CTeletextDecoder::SetFontWidth(int newWidth)
1910 {
1911   if (m_RenderInfo.FontWidth != newWidth)
1912   {
1913     m_RenderInfo.FontWidth = newWidth;
1914     m_TypeTTF.width       = (FT_UShort) m_RenderInfo.FontWidth;
1915
1916     for (int i = 0; i <= 12; i++)
1917       m_RenderInfo.axdrcs[i] = (m_RenderInfo.FontWidth * i + 6) / 12;
1918   }
1919 }
1920
1921 int CTeletextDecoder::GetCurFontWidth()
1922 {
1923   int mx  = (m_RenderInfo.Width)%(40-m_RenderInfo.nofirst);                 // # of unused pixels
1924   int abx = (mx == 0 ? m_RenderInfo.Width+1 : (m_RenderInfo.Width)/(mx+1)); // distance between 'inserted' pixels
1925   int nx  = abx+1-(m_RenderInfo.PosX % (abx+1));                            // # of pixels to next insert
1926   return m_RenderInfo.FontWidth+(((m_RenderInfo.PosX+m_RenderInfo.FontWidth+1) <= m_RenderInfo.Width && nx <= m_RenderInfo.FontWidth+1) ? 1 : 0);
1927 }
1928
1929 void CTeletextDecoder::SetPosX(int column)
1930 {
1931   m_RenderInfo.PosX = 0;
1932
1933   for (int i = 0; i < column-m_RenderInfo.nofirst; i++)
1934     m_RenderInfo.PosX += GetCurFontWidth();
1935 }
1936
1937 void CTeletextDecoder::ClearBB(color_t Color)
1938 {
1939   SDL_memset4(m_TextureBuffer + (m_RenderInfo.Height-m_YOffset)*m_RenderInfo.Width, Color, m_RenderInfo.Width*m_RenderInfo.Height);
1940 }
1941
1942 void CTeletextDecoder::ClearFB(color_t Color)
1943 {
1944   SDL_memset4(m_TextureBuffer + m_RenderInfo.Width*m_YOffset, Color, m_RenderInfo.Width*m_RenderInfo.Height);
1945 }
1946
1947 void CTeletextDecoder::FillBorder(color_t Color)
1948 {
1949   FillRect(m_TextureBuffer + (m_RenderInfo.Height-m_YOffset)*m_RenderInfo.Width, m_RenderInfo.Width, 0, 25*m_RenderInfo.FontHeight, m_RenderInfo.Width, m_RenderInfo.Height-(25*m_RenderInfo.FontHeight), Color);
1950   FillRect(m_TextureBuffer + m_RenderInfo.Width*m_YOffset, m_RenderInfo.Width, 0, 25*m_RenderInfo.FontHeight, m_RenderInfo.Width, m_RenderInfo.Height-(25*m_RenderInfo.FontHeight), Color);
1951 }
1952
1953 void CTeletextDecoder::FillRect(color_t *buffer, int xres, int x, int y, int w, int h, color_t Color)
1954 {
1955   if (!buffer) return;
1956
1957   color_t *p = buffer + x + y * xres;
1958
1959   if (w > 0)
1960   {
1961     for ( ; h > 0 ; h--)
1962     {
1963       SDL_memset4(p, Color, w);
1964       p += xres;
1965     }
1966   }
1967 }
1968
1969 void CTeletextDecoder::DrawVLine(color_t *lfb, int xres, int x, int y, int l, color_t color)
1970 {
1971   if (!lfb) return;
1972   color_t *p = lfb + x + y * xres;
1973
1974   for ( ; l > 0 ; l--)
1975   {
1976     *p = color;
1977     p += xres;
1978   }
1979 }
1980
1981 void CTeletextDecoder::DrawHLine(color_t *lfb, int xres,int x, int y, int l, color_t color)
1982 {
1983   if (!lfb) return;
1984   if (l > 0)
1985     SDL_memset4(lfb + x + y * xres, color, l);
1986 }
1987
1988 void CTeletextDecoder::RenderDRCS(int xres,
1989                                  unsigned char *s,  /* pointer to char data, parity undecoded */
1990                                  color_t *d,  /* pointer to frame buffer of top left pixel */
1991                                  unsigned char *ax, /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */
1992                                  color_t fgcolor, color_t bgcolor)
1993 {
1994   if (d == NULL) return;
1995
1996   unsigned char *ay = ax + 13; /* array[0..10] of y-offsets for each pixel */
1997
1998   for (int y = 0; y < 10; y++) /* 10*2 bytes a 6 pixels per char definition */
1999   {
2000     unsigned char c1 = deparity[*s++];
2001     unsigned char c2 = deparity[*s++];
2002     int h = ay[y+1] - ay[y];
2003
2004     if (!h)
2005       continue;
2006     if (((c1 == ' ') && (*(s-2) != ' ')) || ((c2 == ' ') && (*(s-1) != ' '))) /* parity error: stop decoding FIXME */
2007       return;
2008     for (int bit = 0x20, x = 0;
2009         bit;
2010         bit >>= 1, x++)  /* bit mask (MSB left), column counter */
2011     {
2012       color_t f1 = (c1 & bit) ? fgcolor : bgcolor;
2013       color_t f2 = (c2 & bit) ? fgcolor : bgcolor;
2014       for (int i = 0; i < h; i++)
2015       {
2016         if (ax[x+1] > ax[x])
2017           SDL_memset4(d + ax[x], f1, ax[x+1] - ax[x]);
2018         if (ax[x+7] > ax[x+6])
2019           SDL_memset4(d + ax[x+6], f2, ax[x+7] - ax[x+6]); /* 2nd byte 6 pixels to the right */
2020         d += xres;
2021       }
2022       d -= h * xres;
2023     }
2024     d += h * xres;
2025   }
2026 }
2027
2028 void CTeletextDecoder::FillRectMosaicSeparated(color_t *lfb, int xres,int x, int y, int w, int h, color_t fgcolor, color_t bgcolor, int set)
2029 {
2030   if (!lfb) return;
2031   FillRect(lfb,xres,x, y, w, h, bgcolor);
2032   if (set)
2033   {
2034     FillRect(lfb,xres,x+1, y+1, w-2, h-2, fgcolor);
2035   }
2036 }
2037
2038 void CTeletextDecoder::FillTrapez(color_t *lfb, int xres,int x0, int y0, int l0, int xoffset1, int h, int l1, color_t color)
2039 {
2040   color_t *p = lfb + x0 + y0 * xres;
2041   int xoffset, l;
2042
2043   for (int yoffset = 0; yoffset < h; yoffset++)
2044   {
2045     l = l0 + ((l1-l0) * yoffset + h/2) / h;
2046     xoffset = (xoffset1 * yoffset + h/2) / h;
2047     if (l > 0)
2048       SDL_memset4(p + xoffset, color, l);
2049     p += xres;
2050   }
2051 }
2052
2053 void CTeletextDecoder::FlipHorz(color_t *lfb, int xres,int x, int y, int w, int h)
2054 {
2055   color_t buf[2048];
2056   color_t *p = lfb + x + y * xres;
2057   int w1,h1;
2058
2059   for (h1 = 0 ; h1 < h ; h1++)
2060   {
2061     SDL_memcpy4(buf,p,w);
2062     for (w1 = 0 ; w1 < w ; w1++)
2063     {
2064       *(p+w1) = buf[w-(w1+1)];
2065     }
2066     p += xres;
2067   }
2068 }
2069
2070 void CTeletextDecoder::FlipVert(color_t *lfb, int xres,int x, int y, int w, int h)
2071 {
2072   color_t buf[2048];
2073   color_t *p = lfb + x + y * xres, *p1, *p2;
2074   int h1;
2075
2076   for (h1 = 0 ; h1 < h/2 ; h1++)
2077   {
2078     p1 = (p+(h1*xres));
2079     p2 = (p+(h-(h1+1))*xres);
2080     SDL_memcpy4(buf, p1, w);
2081     SDL_memcpy4(p1, p2, w);
2082     SDL_memcpy4(p2, buf, w);
2083   }
2084 }
2085
2086 int CTeletextDecoder::ShapeCoord(int param, int curfontwidth, int curFontHeight)
2087 {
2088   switch (param)
2089   {
2090   case S_W13:
2091     return curfontwidth/3;
2092   case S_W12:
2093     return curfontwidth/2;
2094   case S_W23:
2095     return curfontwidth*2/3;
2096   case S_W11:
2097     return curfontwidth;
2098   case S_WM3:
2099     return curfontwidth-3;
2100   case S_H13:
2101     return curFontHeight/3;
2102   case S_H12:
2103     return curFontHeight/2;
2104   case S_H23:
2105     return curFontHeight*2/3;
2106   case S_H11:
2107     return curFontHeight;
2108   default:
2109     return param;
2110   }
2111 }
2112
2113 void CTeletextDecoder::DrawShape(color_t *lfb, int xres, int x, int y, int shapenumber, int curfontwidth, int FontHeight, int curFontHeight, color_t fgcolor, color_t bgcolor, bool clear)
2114 {
2115   if (!lfb || shapenumber < 0x20 || shapenumber > 0x7e || (shapenumber == 0x7e && clear))
2116     return;
2117
2118   unsigned char *p = aShapes[shapenumber - 0x20];
2119
2120   if (*p == S_INV)
2121   {
2122     int t = fgcolor;
2123     fgcolor = bgcolor;
2124     bgcolor = t;
2125     p++;
2126   }
2127
2128   if (clear)
2129     FillRect(lfb, xres, x, y, curfontwidth, FontHeight, bgcolor);
2130
2131   while (*p != S_END)
2132   {
2133     switch (*p++)
2134     {
2135     case S_FHL:
2136     {
2137       int offset = ShapeCoord(*p++, curfontwidth, curFontHeight);
2138       DrawHLine(lfb, xres, x, y + offset, curfontwidth, fgcolor);
2139       break;
2140     }
2141     case S_FVL:
2142     {
2143       int offset = ShapeCoord(*p++, curfontwidth, curFontHeight);
2144       DrawVLine(lfb,xres,x + offset, y, FontHeight, fgcolor);
2145       break;
2146     }
2147     case S_FLH:
2148       FlipHorz(lfb,xres,x,y,curfontwidth, FontHeight);
2149       break;
2150     case S_FLV:
2151       FlipVert(lfb,xres,x,y,curfontwidth, FontHeight);
2152       break;
2153     case S_BOX:
2154     {
2155       int xo = ShapeCoord(*p++, curfontwidth, curFontHeight);
2156       int yo = ShapeCoord(*p++, curfontwidth, curFontHeight);
2157       int w = ShapeCoord(*p++, curfontwidth, curFontHeight);
2158       int h = ShapeCoord(*p++, curfontwidth, curFontHeight);
2159       FillRect(lfb,xres,x + xo, y + yo, w, h, fgcolor);
2160       break;
2161     }
2162     case S_TRA:
2163     {
2164       int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2165       int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2166       int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2167       int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2168       int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2169       int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2170       FillTrapez(lfb, xres,x + x0, y + y0, l0, x1-x0, y1-y0, l1, fgcolor);
2171       break;
2172     }
2173     case S_BTR:
2174     {
2175       int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2176       int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2177       int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2178       int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2179       int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2180       int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2181       FillTrapez(lfb, xres, x + x0, y + y0, l0, x1-x0, y1-y0, l1, bgcolor);
2182       break;
2183     }
2184     case S_LNK:
2185     {
2186       DrawShape(lfb,xres,x, y, ShapeCoord(*p, curfontwidth, curFontHeight), curfontwidth, FontHeight, curFontHeight, fgcolor, bgcolor, false);
2187       break;
2188     }
2189     default:
2190       break;
2191     }
2192   }
2193 }
2194
2195 void CTeletextDecoder::RenderCharIntern(TextRenderInfo_t* RenderInfo, int Char, TextPageAttr_t *Attribute, int zoom, int yoffset)
2196 {
2197   int Row, Pitch;
2198   int glyph;
2199   color_t bgcolor, fgcolor;
2200   int factor, xfactor;
2201   unsigned char *sbitbuffer;
2202
2203   int national_subset_local = m_txtCache->NationalSubset;
2204   int curfontwidth          = GetCurFontWidth();
2205   int t                     = curfontwidth;
2206   m_RenderInfo.PosX        += t;
2207   int curfontwidth2         = GetCurFontWidth();
2208   m_RenderInfo.PosX        -= t;
2209   int alphachar             = RenderChar(m_TextureBuffer+(yoffset)*m_RenderInfo.Width, m_RenderInfo.Width, Char, &m_RenderInfo.PosX, m_RenderInfo.PosY, Attribute, zoom > 0, curfontwidth, curfontwidth2, m_RenderInfo.FontHeight, m_RenderInfo.TranspMode, m_RenderInfo.axdrcs, m_Ascender);
2210   if (alphachar <= 0) return;
2211
2212   if (zoom && Attribute->doubleh)
2213     factor = 4;
2214   else if (zoom || Attribute->doubleh)
2215     factor = 2;
2216   else
2217     factor = 1;
2218
2219   fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg);
2220   if (m_RenderInfo.TranspMode && m_RenderInfo.PosY < 24*m_RenderInfo.FontHeight)
2221   {
2222     bgcolor = GetColorRGB(TXT_ColorTransp);
2223   }
2224   else
2225   {
2226     bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg);
2227   }
2228
2229   if (Attribute->doublew)
2230   {
2231     curfontwidth += curfontwidth2;
2232     xfactor = 2;
2233   }
2234   else
2235     xfactor = 1;
2236
2237   if (!(glyph = FT_Get_Char_Index(m_Face, alphachar)))
2238   {
2239     CLog::Log(LOGERROR, "%s:  <FT_Get_Char_Index for Char %x \"%c\" failed", __FUNCTION__, alphachar, alphachar);
2240
2241     FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, factor*m_RenderInfo.FontHeight, bgcolor);
2242     m_RenderInfo.PosX += curfontwidth;
2243     return;
2244   }
2245
2246   if (FTC_SBitCache_Lookup(m_Cache, &m_TypeTTF, glyph, &m_sBit, NULL) != 0)
2247   {
2248     FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, m_RenderInfo.FontHeight, bgcolor);
2249     m_RenderInfo.PosX += curfontwidth;
2250     return;
2251   }
2252
2253   /* render char */
2254   sbitbuffer = m_sBit->buffer;
2255   unsigned char localbuffer[1000]; // should be enough to store one character-bitmap...
2256   // add diacritical marks
2257   if (Attribute->diacrit)
2258   {
2259     FTC_SBit sbit_diacrit;
2260
2261     if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) || (national_subset_local == NAT_UA))
2262       Char = G2table[1][0x20+ Attribute->diacrit];
2263     else if (national_subset_local == NAT_GR)
2264       Char = G2table[2][0x20+ Attribute->diacrit];
2265     else if (national_subset_local == NAT_HB)
2266       Char = G2table[3][0x20+ Attribute->diacrit];
2267     else if (national_subset_local == NAT_AR)
2268       Char = G2table[4][0x20+ Attribute->diacrit];
2269     else
2270       Char = G2table[0][0x20+ Attribute->diacrit];
2271     if ((glyph = FT_Get_Char_Index(m_Face, Char)))
2272     {
2273       if (FTC_SBitCache_Lookup(m_Cache, &m_TypeTTF, glyph, &sbit_diacrit, NULL) == 0)
2274       {
2275         sbitbuffer = localbuffer;
2276         memcpy(sbitbuffer,m_sBit->buffer,m_sBit->pitch*m_sBit->height);
2277
2278         for (Row = 0; Row < m_sBit->height; Row++)
2279         {
2280           for (Pitch = 0; Pitch < m_sBit->pitch; Pitch++)
2281           {
2282             if (sbit_diacrit->pitch > Pitch && sbit_diacrit->height > Row)
2283               sbitbuffer[Row*m_sBit->pitch+Pitch] |= sbit_diacrit->buffer[Row*m_sBit->pitch+Pitch];
2284           }
2285         }
2286       }
2287     }
2288   }
2289
2290   int backupTTFshiftY = m_RenderInfo.TTFShiftY;
2291   if (national_subset_local == NAT_AR)
2292       m_RenderInfo.TTFShiftY = backupTTFshiftY - 2; // for arabic TTF font should be shifted up slightly
2293
2294   color_t *p;
2295   int f; /* running counter for zoom factor */
2296   int he = m_sBit->height; // sbit->height should not be altered, I guess
2297   Row = factor * (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY);
2298   if (Row < 0)
2299   {
2300     sbitbuffer  -= m_sBit->pitch*Row;
2301     he += Row;
2302     Row = 0;
2303   }
2304   else
2305   {
2306     FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, Row, bgcolor); /* fill upper margin */
2307   }
2308
2309   if (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY + he > m_RenderInfo.FontHeight)
2310     he = m_RenderInfo.FontHeight - m_Ascender + m_sBit->top - m_RenderInfo.TTFShiftY; /* limit char height to defined/calculated FontHeight */
2311   if (he < 0) he = m_RenderInfo.FontHeight;
2312
2313   p = m_TextureBuffer + m_RenderInfo.PosX + (yoffset + m_RenderInfo.PosY + Row) * m_RenderInfo.Width; /* running pointer into framebuffer */
2314   for (Row = he; Row; Row--) /* row counts up, but down may be a little faster :) */
2315   {
2316     int pixtodo = m_sBit->width;
2317     color_t *pstart = p;
2318
2319     for (int Bit = xfactor * (m_sBit->left + m_RenderInfo.TTFShiftX); Bit > 0; Bit--) /* fill left margin */
2320     {
2321       for (f = factor-1; f >= 0; f--)
2322         *(p + f*m_RenderInfo.Width) = bgcolor;
2323       p++;
2324     }
2325
2326     for (Pitch = m_sBit->pitch; Pitch; Pitch--)
2327     {
2328       for (int Bit = 0x80; Bit; Bit >>= 1)
2329       {
2330         color_t color;
2331
2332         if (--pixtodo < 0)
2333           break;
2334
2335         if (*sbitbuffer & Bit) /* bit set -> foreground */
2336           color = fgcolor;
2337         else /* bit not set -> background */
2338           color = bgcolor;
2339
2340         for (f = factor-1; f >= 0; f--)
2341           *(p + f*m_RenderInfo.Width) = color;
2342         p++;
2343
2344         if (xfactor > 1) /* double width */
2345         {
2346           for (f = factor-1; f >= 0; f--)
2347             *(p + f*m_RenderInfo.Width) = color;
2348           p++;
2349         }
2350       }
2351       sbitbuffer++;
2352     }
2353     for (int Bit = (curfontwidth - xfactor*(m_sBit->width + m_sBit->left + m_RenderInfo.TTFShiftX));
2354         Bit > 0; Bit--) /* fill rest of char width */
2355     {
2356       for (f = factor-1; f >= 0; f--)
2357         *(p + f*m_RenderInfo.Width) = bgcolor;
2358       p++;
2359     }
2360
2361     p = pstart + factor*m_RenderInfo.Width;
2362   }
2363
2364   Row = m_Ascender - m_sBit->top + he + m_RenderInfo.TTFShiftY;
2365   FillRect(m_TextureBuffer,
2366            m_RenderInfo.Width,
2367            m_RenderInfo.PosX,
2368            m_RenderInfo.PosY + yoffset + Row * factor,
2369            curfontwidth,
2370            (m_RenderInfo.FontHeight - Row) * factor,
2371            bgcolor); /* fill lower margin */
2372
2373   if (Attribute->underline)
2374     FillRect(m_TextureBuffer,
2375             m_RenderInfo.Width,
2376             m_RenderInfo.PosX,
2377             m_RenderInfo.PosY + yoffset + (m_RenderInfo.FontHeight-2)* factor,
2378             curfontwidth,
2379             2*factor,
2380             fgcolor); /* underline char */
2381
2382   m_RenderInfo.PosX      += curfontwidth;
2383   m_RenderInfo.TTFShiftY  = backupTTFshiftY; // restore TTFShiftY
2384 }
2385
2386 int CTeletextDecoder::RenderChar(color_t *buffer,    // pointer to render buffer, min. FontHeight*2*xres
2387                                 int xres,                 // length of 1 line in render buffer
2388                                 int Char,                 // character to render
2389                                 int *pPosX,               // left border for rendering relative to *buffer, will be set to right border after rendering
2390                                 int PosY,                 // vertical position of char in *buffer
2391                                 TextPageAttr_t *Attribute,// Attributes of Char
2392                                 bool zoom,                // 1= character will be rendered in double height
2393                                 int curfontwidth,         // rendering width of character
2394                                 int curfontwidth2,        // rendering width of next character (needed for doublewidth)
2395                                 int FontHeight,           // height of character
2396                                 bool transpmode,          // 1= transparent display
2397                                 unsigned char *axdrcs,    // width and height of DRCS-chars
2398                                 int Ascender)             // Ascender of font
2399 {
2400   color_t bgcolor, fgcolor;
2401   int factor, xfactor;
2402   int national_subset_local = m_txtCache->NationalSubset;
2403   int ymosaic[4];
2404   ymosaic[0] = 0; /* y-offsets for 2*3 mosaic */
2405   ymosaic[1] = (FontHeight + 1) / 3;
2406   ymosaic[2] = (FontHeight * 2 + 1) / 3;
2407   ymosaic[3] = FontHeight;
2408
2409   if (Attribute->setX26)
2410   {
2411     national_subset_local = 0; // no national subset
2412   }
2413
2414   // G0+G2 set designation
2415   if (Attribute->setG0G2 != 0x3f)
2416   {
2417     switch (Attribute->setG0G2)
2418     {
2419       case 0x20 :
2420         national_subset_local = NAT_SC;
2421         break;
2422       case 0x24 :
2423         national_subset_local = NAT_RB;
2424         break;
2425       case 0x25 :
2426         national_subset_local = NAT_UA;
2427         break;
2428       case 0x37:
2429         national_subset_local = NAT_GR;
2430         break;
2431       case 0x55:
2432         national_subset_local = NAT_HB;
2433         break;
2434       case 0x47:
2435       case 0x57:
2436         national_subset_local = NAT_AR;
2437         break;
2438       default:
2439         national_subset_local = CountryConversionTable[Attribute->setG0G2 & 0x07];
2440         break;
2441     }
2442   }
2443
2444   if (Attribute->charset == C_G0S) // use secondary charset
2445     national_subset_local = m_txtCache->NationalSubsetSecondary;
2446   if (zoom && Attribute->doubleh)
2447     factor = 4;
2448   else if (zoom || Attribute->doubleh)
2449     factor = 2;
2450   else
2451     factor = 1;
2452
2453   if (Attribute->doublew)
2454   {
2455     curfontwidth += curfontwidth2;
2456     xfactor = 2;
2457   }
2458   else
2459     xfactor = 1;
2460
2461   if (Char == 0xFF)  /* skip doubleheight chars in lower line */
2462   {
2463     *pPosX += curfontwidth;
2464     return -1;
2465   }
2466
2467   /* get colors */
2468   if (Attribute->inverted)
2469   {
2470     int t = Attribute->fg;
2471     Attribute->fg = Attribute->bg;
2472     Attribute->bg = t;
2473   }
2474   fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg);
2475   if (transpmode == true && PosY < 24*FontHeight)
2476   {
2477     bgcolor = GetColorRGB(TXT_ColorTransp);
2478   }
2479   else
2480   {
2481     bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg);
2482   }
2483
2484   /* handle mosaic */
2485   if ((Attribute->charset == C_G1C || Attribute->charset == C_G1S) &&
2486      ((Char&0xA0) == 0x20))
2487   {
2488     int w1 = (curfontwidth / 2 ) *xfactor;
2489     int w2 = (curfontwidth - w1) *xfactor;
2490
2491     Char = (Char & 0x1f) | ((Char & 0x40) >> 1);
2492     if (Attribute->charset == C_G1S) /* separated mosaic */
2493     {
2494       for (int y = 0; y < 3; y++)
2495       {
2496         FillRectMosaicSeparated(buffer, xres,*pPosX,      PosY +  ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x01);
2497         FillRectMosaicSeparated(buffer, xres,*pPosX + w1, PosY +  ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x02);
2498         Char >>= 2;
2499       }
2500     }
2501     else
2502     {
2503       for (int y = 0; y < 3; y++)
2504       {
2505         FillRect(buffer, xres, *pPosX,      PosY + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x01) ? fgcolor : bgcolor);
2506         FillRect(buffer, xres, *pPosX + w1, PosY + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x02) ? fgcolor : bgcolor);
2507         Char >>= 2;
2508       }
2509     }
2510
2511     *pPosX += curfontwidth;
2512     return 0;;
2513   }
2514
2515   if (Attribute->charset == C_G3)
2516   {
2517     if (Char < 0x20 || Char > 0x7d)
2518     {
2519       Char = 0x20;
2520     }
2521     else
2522     {
2523       if (*aShapes[Char - 0x20] == S_CHR)
2524       {
2525         unsigned char *p = aShapes[Char - 0x20];
2526         Char = (*(p+1) <<8) + (*(p+2));
2527       }
2528       else if (*aShapes[Char - 0x20] == S_ADT)
2529       {
2530         if (buffer)
2531         {
2532           int x,y,f,c;
2533           color_t* p = buffer + *pPosX + PosY* xres;
2534           for (y=0; y<FontHeight;y++)
2535           {
2536             for (f=0; f<factor; f++)
2537             {
2538               for (x=0; x<curfontwidth*xfactor;x++)
2539               {
2540                 c = (y&4 ? (x/3)&1 :((x+3)/3)&1);
2541                 *(p+x) = (c ? fgcolor : bgcolor);
2542               }
2543               p += xres;
2544             }
2545           }
2546         }
2547         *pPosX += curfontwidth;
2548         return 0;
2549       }
2550       else
2551       {
2552         DrawShape(buffer, xres,*pPosX, PosY, Char, curfontwidth, FontHeight, factor*FontHeight, fgcolor, bgcolor, true);
2553         *pPosX += curfontwidth;
2554         return 0;
2555       }
2556     }
2557   }
2558   else if (Attribute->charset >= C_OFFSET_DRCS)
2559   {
2560     TextCachedPage_t *pcache = m_txtCache->astCachetable[(Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs][Attribute->charset & 0x0f];
2561     if (pcache)
2562     {
2563       unsigned char drcs_data[23*40];
2564       g_application.m_pPlayer->LoadPage((Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs, Attribute->charset & 0x0f, drcs_data);
2565       unsigned char *p;
2566       if (Char < 23*2)
2567         p = drcs_data + 20*Char;
2568       else if (pcache->pageinfo.p24)
2569         p = pcache->pageinfo.p24 + 20*(Char - 23*2);
2570       else
2571       {
2572         FillRect(buffer, xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2573         *pPosX += curfontwidth;
2574         return 0;
2575       }
2576       axdrcs[12] = curfontwidth; /* adjust last x-offset according to position, FIXME: double width */
2577       RenderDRCS(xres, p, buffer + *pPosX + PosY * xres, axdrcs, fgcolor, bgcolor);
2578     }
2579     else
2580     {
2581       FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2582     }
2583     *pPosX += curfontwidth;
2584     return 0;
2585   }
2586   else if (Attribute->charset == C_G2 && Char >= 0x20 && Char <= 0x7F)
2587   {
2588     if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) || (national_subset_local == NAT_UA))
2589       Char = G2table[1][Char-0x20];
2590     else if (national_subset_local == NAT_GR)
2591       Char = G2table[2][Char-0x20];
2592     else if (national_subset_local == NAT_AR)
2593       Char = G2table[3][Char-0x20];
2594     else
2595       Char = G2table[0][Char-0x20];
2596
2597     //if (Char == 0x7F)
2598     //{
2599     //  FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*Ascender, fgcolor);
2600     //  FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor);
2601     //  *pPosX += curfontwidth;
2602     //  return 0;
2603     //}
2604   }
2605   else if (national_subset_local == NAT_SC && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for serbian/croatian */
2606     Char = G0table[0][Char-0x20];
2607   else if (national_subset_local == NAT_RB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for russian/bulgarian */
2608     Char = G0table[1][Char-0x20];
2609   else if (national_subset_local == NAT_UA && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for ukrainian */
2610     Char = G0table[2][Char-0x20];
2611   else if (national_subset_local == NAT_GR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for greek */
2612     Char = G0table[3][Char-0x20];
2613   else if (national_subset_local == NAT_HB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for hebrew */
2614     Char = G0table[4][Char-0x20];
2615   else if (national_subset_local == NAT_AR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for arabic */
2616     Char = G0table[5][Char-0x20];
2617   else
2618   {
2619     /* load char */
2620     switch (Char)
2621     {
2622     case 0x00:
2623     case 0x20:
2624       FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2625       *pPosX += curfontwidth;
2626       return -3;
2627     case 0x23:
2628     case 0x24:
2629       Char = nationaltable23[national_subset_local][Char-0x23];
2630       break;
2631     case 0x40:
2632       Char = nationaltable40[national_subset_local];
2633       break;
2634     case 0x5B:
2635     case 0x5C:
2636     case 0x5D:
2637     case 0x5E:
2638     case 0x5F:
2639     case 0x60:
2640       Char = nationaltable5b[national_subset_local][Char-0x5B];
2641       break;
2642     case 0x7B:
2643     case 0x7C:
2644     case 0x7D:
2645     case 0x7E:
2646       Char = nationaltable7b[national_subset_local][Char-0x7B];
2647       break;
2648     case 0x7F:
2649       FillRect(buffer,xres,*pPosX, PosY , curfontwidth, factor*Ascender, fgcolor);
2650       FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor);
2651       *pPosX += curfontwidth;
2652       return 0;
2653     case 0xE0: /* |- */
2654       DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2655       DrawVLine(buffer,xres,*pPosX, PosY +1, FontHeight -1, fgcolor);
2656       FillRect(buffer,xres,*pPosX +1, PosY +1, curfontwidth-1, FontHeight-1, bgcolor);
2657       *pPosX += curfontwidth;
2658       return 0;
2659     case 0xE1: /* - */
2660       DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2661       FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight-1, bgcolor);
2662       *pPosX += curfontwidth;
2663       return 0;
2664     case 0xE2: /* -| */
2665       DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2666       DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY +1, FontHeight -1, fgcolor);
2667       FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth-1, FontHeight-1, bgcolor);
2668       *pPosX += curfontwidth;
2669       return 0;
2670     case 0xE3: /* |  */
2671       DrawVLine(buffer,xres,*pPosX, PosY, FontHeight, fgcolor);
2672       FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor);
2673       *pPosX += curfontwidth;
2674       return 0;
2675     case 0xE4: /*  | */
2676       DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight, fgcolor);
2677       FillRect(buffer,xres,*pPosX, PosY, curfontwidth -1, FontHeight, bgcolor);
2678       *pPosX += curfontwidth;
2679       return 0;
2680     case 0xE5: /* |_ */
2681       DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2682       DrawVLine(buffer,xres,*pPosX, PosY, FontHeight -1, fgcolor);
2683       FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth-1, FontHeight-1, bgcolor);
2684       *pPosX += curfontwidth;
2685       return 0;
2686     case 0xE6: /* _ */
2687       DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2688       FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight-1, bgcolor);
2689       *pPosX += curfontwidth;
2690       return 0;
2691     case 0xE7: /* _| */
2692       DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2693       DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight -1, fgcolor);
2694       FillRect(buffer,xres,*pPosX, PosY, curfontwidth-1, FontHeight-1, bgcolor);
2695       *pPosX += curfontwidth;
2696       return 0;
2697     case 0xE8: /* Ii */
2698       FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor);
2699       for (int Row=0; Row < curfontwidth/2; Row++)
2700         DrawVLine(buffer,xres,*pPosX + Row, PosY + Row, FontHeight - Row, fgcolor);
2701       *pPosX += curfontwidth;
2702       return 0;
2703     case 0xE9: /* II */
2704       FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, FontHeight, fgcolor);
2705       FillRect(buffer,xres,*pPosX + curfontwidth/2, PosY, (curfontwidth+1)/2, FontHeight, bgcolor);
2706       *pPosX += curfontwidth;
2707       return 0;
2708     case 0xEA: /* ∞  */
2709       FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight, bgcolor);
2710       FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, curfontwidth/2, fgcolor);
2711       *pPosX += curfontwidth;
2712       return 0;
2713     case 0xEB: /* ¨ */
2714       FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight -1, bgcolor);
2715       for (int Row=0; Row < curfontwidth/2; Row++)
2716         DrawHLine(buffer,xres,*pPosX + Row, PosY + Row, curfontwidth - Row, fgcolor);
2717       *pPosX += curfontwidth;
2718       return 0;
2719     case 0xEC: /* -- */
2720       FillRect(buffer, xres,*pPosX, PosY, curfontwidth, curfontwidth/2, fgcolor);
2721       FillRect(buffer, xres,*pPosX, PosY + curfontwidth/2, curfontwidth, FontHeight - curfontwidth/2, bgcolor);
2722       *pPosX += curfontwidth;
2723       return 0;
2724     case 0xED:
2725     case 0xEE:
2726     case 0xEF:
2727     case 0xF0:
2728     case 0xF1:
2729     case 0xF2:
2730     case 0xF3:
2731     case 0xF4:
2732     case 0xF5:
2733     case 0xF6:
2734       Char = arrowtable[Char - 0xED];
2735       break;
2736     default:
2737       break;
2738     }
2739   }
2740   if (Char <= 0x20)
2741   {
2742     FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2743     *pPosX += curfontwidth;
2744     return -2;
2745   }
2746   return Char; // Char is an alphanumeric unicode character
2747 }
2748
2749 TextPageinfo_t* CTeletextDecoder::DecodePage(bool showl25,             // 1=decode Level2.5-graphics
2750                                             unsigned char* PageChar,  // page buffer, min. 25*40
2751                                             TextPageAttr_t *PageAtrb, // attribut buffer, min 25*40
2752                                             bool HintMode,            // 1=show hidden information
2753                                             bool showflof)            // 1=decode FLOF-line
2754 {
2755   int col;
2756   int hold, dhset;
2757   int foreground, background, doubleheight, doublewidth, charset, previous_charset, mosaictype, IgnoreAtBlackBgSubst, concealed, flashmode, boxwin;
2758   unsigned char held_mosaic, *p;
2759   TextCachedPage_t *pCachedPage;
2760
2761   /* copy page to decode buffer */
2762   if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff) /* not cached: do nothing */
2763     return NULL;
2764
2765   if (m_txtCache->ZapSubpageManual)
2766     pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage];
2767   else
2768     pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPageTable[m_txtCache->Page]];
2769   if (!pCachedPage)  /* not cached: do nothing */
2770     return NULL;
2771
2772   g_application.m_pPlayer->LoadPage(m_txtCache->Page, m_txtCache->SubPage, &PageChar[40]);
2773
2774   memcpy(&PageChar[8], pCachedPage->p0, 24); /* header line without TimeString */
2775
2776   TextPageinfo_t* PageInfo = &(pCachedPage->pageinfo);
2777   if (PageInfo->p24)
2778     memcpy(&PageChar[24*40], PageInfo->p24, 40); /* line 25 for FLOF */
2779
2780   /* copy TimeString */
2781   memcpy(&PageChar[32], &m_txtCache->TimeString, 8);
2782
2783   bool boxed;
2784   /* check for newsflash & subtitle */
2785   if (PageInfo->boxed && IsDec(m_txtCache->Page))
2786     boxed = true;
2787   else
2788     boxed = false;
2789
2790
2791   /* modify header */
2792   if (boxed)
2793   {
2794     memset(PageChar, ' ', 40);
2795   }
2796   else
2797   {
2798     memset(PageChar, ' ', 8);
2799     CDVDTeletextTools::Hex2Str((char*)PageChar+3, m_txtCache->Page);
2800     if (m_txtCache->SubPage)
2801     {
2802       *(PageChar+4) ='/';
2803       *(PageChar+5) ='0';
2804       CDVDTeletextTools::Hex2Str((char*)PageChar+6, m_txtCache->SubPage);
2805     }
2806   }
2807
2808   if (!IsDec(m_txtCache->Page))
2809   {
2810     TextPageAttr_t atr = { TXT_ColorWhite  , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f};
2811     if (PageInfo->function == FUNC_MOT) /* magazine organization table */
2812     {
2813       for (col = 0; col < 24*40; col++)
2814         PageAtrb[col] = atr;
2815       for (col = 40; col < 24*40; col++)
2816         PageChar[col] = number2char(PageChar[col]);
2817       return PageInfo; /* don't interpret irregular pages */
2818     }
2819     else if (PageInfo->function == FUNC_GPOP || PageInfo->function == FUNC_POP) /* object definitions */
2820     {
2821       for (int col = 0; col < 24*40; col++)
2822         PageAtrb[col] = atr;
2823
2824       p = PageChar + 40;
2825       for (int row = 1; row < 12; row++)
2826       {
2827         *p++ = number2char(row); /* first column: number (0-9, A-..) */
2828         for (int col = 1; col < 40; col += 3)
2829         {
2830           int d = CDVDTeletextTools::deh24(p);
2831           if (d < 0)
2832           {
2833             memcpy(p, "???", 3);
2834           p += 3;
2835           }
2836           else
2837           {
2838             *p++ = number2char((d >> 6) & 0x1f); /* mode */
2839             *p++ = number2char(d & 0x3f); /* address */
2840             *p++ = number2char((d >> 11) & 0x7f); /* data */
2841           }
2842         }
2843       }
2844       return PageInfo; /* don't interpret irregular pages */
2845     }
2846     else if (PageInfo->function == FUNC_GDRCS || PageInfo->function == FUNC_DRCS) /* character definitions */
2847     {
2848       return PageInfo; /* don't interpret irregular pages */
2849     }
2850     else
2851     {
2852       int h, parityerror = 0;
2853
2854       for (int i = 0; i < 8; i++)
2855         PageAtrb[i] = atr;
2856
2857       /* decode parity/hamming */
2858       for (unsigned int i = 40; i < sizeof(PageChar); i++)
2859       {
2860         PageAtrb[i] = atr;
2861         p = PageChar + i;
2862         h = dehamming[*p];
2863         if (parityerror && h != 0xFF)  /* if no regular page (after any parity error) */
2864           CDVDTeletextTools::Hex2Str((char*)p, h);  /* first try dehamming */
2865         else
2866         {
2867           if (*p == ' ' || deparity[*p] != ' ') /* correct parity */
2868             *p &= 127;
2869           else
2870           {
2871             parityerror = 1;
2872             if (h != 0xFF)  /* first parity error: try dehamming */
2873               CDVDTeletextTools::Hex2Str((char*)p, h);
2874             else
2875               *p = ' ';
2876           }
2877         }
2878       }
2879       if (parityerror)
2880       {
2881         return PageInfo; /* don't interpret irregular pages */
2882       }
2883     }
2884   }
2885   int mosaic_pending,esc_pending;
2886   /* decode */
2887   for (int row = 0; row < ((showflof && PageInfo->p24) ? 25 : 24); row++)
2888   {
2889     /* start-of-row default conditions */
2890     foreground   = TXT_ColorWhite;
2891     background   = TXT_ColorBlack;
2892     doubleheight = 0;
2893     doublewidth  = 0;
2894     charset      = previous_charset = C_G0P; // remember charset for switching back after mosaic charset was used
2895     mosaictype   = 0;
2896     concealed    = 0;
2897     flashmode    = 0;
2898     hold         = 0;
2899     boxwin       = 0;
2900     held_mosaic  = ' ';
2901     dhset        = 0;
2902     IgnoreAtBlackBgSubst = 0;
2903     mosaic_pending = esc_pending = 0; // we need to render at least one mosaic char if 'esc' is received immediatly after mosac charset switch on
2904
2905     if (boxed && memchr(&PageChar[row*40], start_box, 40) == 0)
2906     {
2907       foreground = TXT_ColorTransp;
2908       background = TXT_ColorTransp;
2909     }
2910
2911     for (int col = 0; col < 40; col++)
2912     {
2913       int index = row*40 + col;
2914
2915       PageAtrb[index].fg = foreground;
2916       PageAtrb[index].bg = background;
2917       PageAtrb[index].charset = charset;
2918       PageAtrb[index].doubleh = doubleheight;
2919       PageAtrb[index].doublew = (col < 39 ? doublewidth : 0);
2920       PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
2921       PageAtrb[index].concealed = concealed;
2922       PageAtrb[index].flashing  = flashmode;
2923       PageAtrb[index].boxwin    = boxwin;
2924       PageAtrb[index].inverted  = 0; // only relevant for Level 2.5
2925       PageAtrb[index].underline = 0; // only relevant for Level 2.5
2926       PageAtrb[index].diacrit   = 0; // only relevant for Level 2.5
2927       PageAtrb[index].setX26    = 0; // only relevant for Level 2.5
2928       PageAtrb[index].setG0G2   = 0x3f; // only relevant for Level 2.5
2929
2930       if (PageChar[index] < ' ')
2931       {
2932         if (esc_pending) { // mosaic char has been rendered and we can switch charsets
2933           charset = previous_charset;
2934           if (charset == C_G0P)
2935             charset = previous_charset = C_G0S;
2936           else if (charset == C_G0S)
2937             charset = previous_charset = C_G0P;
2938           esc_pending = 0;
2939         }
2940         switch (PageChar[index])
2941         {
2942         case alpha_black:
2943         case alpha_red:
2944         case alpha_green:
2945         case alpha_yellow:
2946         case alpha_blue:
2947         case alpha_magenta:
2948         case alpha_cyan:
2949         case alpha_white:
2950           concealed = 0;
2951           foreground = PageChar[index] - alpha_black + TXT_ColorBlack;
2952           if (col == 0 && PageChar[index] == alpha_white)
2953             PageAtrb[index].fg = TXT_ColorBlack; // indicate level 1 color change on column 0; (hack)
2954           if ((charset!=C_G0P) && (charset!=C_G0S)) // we need to change charset to state it was before mosaic
2955             charset = previous_charset;
2956           break;
2957
2958         case flash:
2959           flashmode = 1;
2960           break;
2961
2962         case steady:
2963           flashmode = 0;
2964           PageAtrb[index].flashing = 0;
2965           break;
2966
2967         case end_box:
2968           boxwin = 0;
2969           IgnoreAtBlackBgSubst = 0;
2970           break;
2971
2972         case start_box:
2973           if (!boxwin)
2974             boxwin = 1;
2975           break;
2976
2977         case normal_size:
2978           doubleheight = 0;
2979           doublewidth = 0;
2980           PageAtrb[index].doubleh = doubleheight;
2981           PageAtrb[index].doublew = doublewidth;
2982           break;
2983
2984         case double_height:
2985           if (row < 23)
2986           {
2987             doubleheight = 1;
2988             dhset = 1;
2989           }
2990           doublewidth = 0;
2991
2992           break;
2993
2994         case double_width:
2995           if (col < 39)
2996             doublewidth = 1;
2997           doubleheight = 0;
2998           break;
2999
3000         case double_size:
3001           if (row < 23)
3002           {
3003             doubleheight = 1;
3004             dhset = 1;
3005           }
3006           if (col < 39)
3007             doublewidth = 1;
3008           break;
3009
3010         case mosaic_black:
3011         case mosaic_red:
3012         case mosaic_green:
3013         case mosaic_yellow:
3014         case mosaic_blue:
3015         case mosaic_magenta:
3016         case mosaic_cyan:
3017         case mosaic_white:
3018           concealed = 0;
3019           foreground = PageChar[index] - mosaic_black + TXT_ColorBlack;
3020           if ((charset==C_G0P) || (charset==C_G0S))
3021             previous_charset=charset;
3022           charset = mosaictype ? C_G1S : C_G1C;
3023           mosaic_pending = 1;
3024           break;
3025
3026         case conceal:
3027           PageAtrb[index].concealed = 1;
3028           concealed = 1;
3029           if (!HintMode)
3030           {
3031             foreground = background;
3032             PageAtrb[index].fg = foreground;
3033           }
3034           break;
3035
3036         case contiguous_mosaic:
3037           mosaictype = 0;
3038           if (charset == C_G1S)
3039           {
3040             charset = C_G1C;
3041             PageAtrb[index].charset = charset;
3042           }
3043           break;
3044
3045         case separated_mosaic:
3046           mosaictype = 1;
3047           if (charset == C_G1C)
3048           {
3049             charset = C_G1S;
3050             PageAtrb[index].charset = charset;
3051           }
3052           break;
3053
3054         case esc:
3055           if (!mosaic_pending) { // if mosaic is pending we need to wait before mosaic arrives
3056             if ((charset != C_G0P) && (charset != C_G0S)) // we need to switch to charset which was active before mosaic
3057               charset = previous_charset;
3058             if (charset == C_G0P)
3059               charset = previous_charset = C_G0S;
3060             else if (charset == C_G0S)
3061               charset = previous_charset = C_G0P;
3062           } else esc_pending = 1;
3063           break;
3064
3065         case black_background:
3066           background = TXT_ColorBlack;
3067           IgnoreAtBlackBgSubst = 0;
3068           PageAtrb[index].bg = background;
3069           PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
3070           break;
3071
3072         case new_background:
3073           background = foreground;
3074           if (background == TXT_ColorBlack)
3075             IgnoreAtBlackBgSubst = 1;
3076           else
3077             IgnoreAtBlackBgSubst = 0;
3078           PageAtrb[index].bg = background;
3079           PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
3080           break;
3081
3082         case hold_mosaic:
3083           hold = 1;
3084           break;
3085
3086         case release_mosaic:
3087           hold = 2;
3088           break;
3089         }
3090
3091         /* handle spacing attributes */
3092         if (hold && (PageAtrb[index].charset == C_G1C || PageAtrb[index].charset == C_G1S))
3093           PageChar[index] = held_mosaic;
3094         else
3095           PageChar[index] = ' ';
3096
3097         if (hold == 2)
3098           hold = 0;
3099       }
3100       else /* char >= ' ' */
3101       {
3102         mosaic_pending = 0; // charset will be switched next if esc_pending
3103         /* set new held-mosaic char */
3104         if ((charset == C_G1C || charset == C_G1S) &&
3105            ((PageChar[index]&0xA0) == 0x20))
3106           held_mosaic = PageChar[index];
3107         if (PageAtrb[index].doubleh)
3108           PageChar[index + 40] = 0xFF;
3109
3110       }
3111       if (!(charset == C_G1C || charset == C_G1S))
3112         held_mosaic = ' '; /* forget if outside mosaic */
3113
3114     } /* for col */
3115
3116     /* skip row if doubleheight */
3117     if (row < 23 && dhset)
3118     {
3119       for (int col = 0; col < 40; col++)
3120       {
3121         int index = row*40 + col;
3122         PageAtrb[index+40].bg = PageAtrb[index].bg;
3123         PageAtrb[index+40].fg = TXT_ColorWhite;
3124         if (!PageAtrb[index].doubleh)
3125           PageChar[index+40] = ' ';
3126         PageAtrb[index+40].flashing = 0;
3127         PageAtrb[index+40].charset = C_G0P;
3128         PageAtrb[index+40].doubleh = 0;
3129         PageAtrb[index+40].doublew = 0;
3130         PageAtrb[index+40].IgnoreAtBlackBgSubst = 0;
3131         PageAtrb[index+40].concealed = 0;
3132         PageAtrb[index+40].flashing  = 0;
3133         PageAtrb[index+40].boxwin    = PageAtrb[index].boxwin;
3134       }
3135       row++;
3136     }
3137   } /* for row */
3138   m_txtCache->FullScrColor = TXT_ColorBlack;
3139
3140   if (showl25)
3141     Eval_l25(PageChar, PageAtrb, HintMode);
3142
3143   /* handle Black Background Color Substitution and transparency (CLUT1#0) */
3144   {
3145     int o = 0;
3146     char bitmask ;
3147
3148     for (int r = 0; r < 25; r++)
3149     {
3150       for (int c = 0; c < 40; c++)
3151       {
3152         bitmask = (PageAtrb[o].bg == 0x08 ? 0x08 : 0x00) | (m_txtCache->FullRowColor[r] == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed;
3153         switch (bitmask)
3154         {
3155           case 0x08:
3156           case 0x0b:
3157             if (m_txtCache->FullRowColor[r] == 0x08)
3158               PageAtrb[o].bg = m_txtCache->FullScrColor;
3159             else
3160               PageAtrb[o].bg = m_txtCache->FullRowColor[r];
3161             break;
3162           case 0x01:
3163           case 0x05:
3164           case 0x09:
3165           case 0x0a:
3166           case 0x0c:
3167           case 0x0d:
3168           case 0x0e:
3169           case 0x0f:
3170             PageAtrb[o].bg = TXT_ColorTransp;
3171             break;
3172         }
3173         bitmask = (PageAtrb[o].fg  == 0x08 ? 0x08 : 0x00) | (m_txtCache->FullRowColor[r] == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed;
3174         switch (bitmask)
3175         {
3176           case 0x08:
3177           case 0x0b:
3178             if (m_txtCache->FullRowColor[r] == 0x08)
3179               PageAtrb[o].fg = m_txtCache->FullScrColor;
3180             else
3181               PageAtrb[o].fg = m_txtCache->FullRowColor[r];
3182             break;
3183           case 0x01:
3184           case 0x05:
3185           case 0x09:
3186           case 0x0a:
3187           case 0x0c:
3188           case 0x0d:
3189           case 0x0e:
3190           case 0x0f:
3191             PageAtrb[o].fg = TXT_ColorTransp;
3192             break;
3193         }
3194         o++;
3195       }
3196     }
3197   }
3198   return PageInfo;
3199 }
3200
3201 void CTeletextDecoder::Eval_l25(unsigned char* PageChar, TextPageAttr_t *PageAtrb, bool HintMode)
3202 {
3203   memset(m_txtCache->FullRowColor, 0, sizeof(m_txtCache->FullRowColor));
3204   m_txtCache->FullScrColor = TXT_ColorBlack;
3205   m_txtCache->ColorTable   = NULL;
3206
3207   if (!m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage])
3208     return;
3209
3210   /* normal page */
3211   if (IsDec(m_txtCache->Page))
3212   {
3213     unsigned char APx0, APy0, APx, APy;
3214     TextPageinfo_t *pi      = &(m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage]->pageinfo);
3215     TextCachedPage_t *pmot  = m_txtCache->astCachetable[(m_txtCache->Page & 0xf00) | 0xfe][0];
3216     int p26Received         = 0;
3217     int BlackBgSubst        = 0;
3218     int ColorTableRemapping = 0;
3219
3220     m_txtCache->pop = m_txtCache->gpop = m_txtCache->drcs = m_txtCache->gdrcs = 0;
3221
3222     if (pi->ext)
3223     {
3224       TextExtData_t *e = pi->ext;
3225
3226       if (e->p26[0])
3227         p26Received = 1;
3228
3229       if (e->p27)
3230       {
3231         Textp27_t *p27 = e->p27;
3232         if (p27[0].l25)
3233           m_txtCache->gpop = p27[0].page;
3234         if (p27[1].l25)
3235           m_txtCache->pop = p27[1].page;
3236         if (p27[2].l25)
3237           m_txtCache->gdrcs = p27[2].page;
3238         if (p27[3].l25)
3239           m_txtCache->drcs = p27[3].page;
3240       }
3241
3242       if (e->p28Received)
3243       {
3244         m_txtCache->ColorTable              = e->bgr;
3245         BlackBgSubst                      = e->BlackBgSubst;
3246         ColorTableRemapping               = e->ColorTableRemapping;
3247         memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor));
3248         m_txtCache->FullScrColor            = e->DefScreenColor;
3249         m_txtCache->NationalSubset          = SetNational(e->DefaultCharset);
3250         m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset);
3251       } /* e->p28Received */
3252     }
3253
3254     if (!m_txtCache->ColorTable && m_txtCache->astP29[m_txtCache->Page >> 8])
3255     {
3256       TextExtData_t *e                    = m_txtCache->astP29[m_txtCache->Page >> 8];
3257       m_txtCache->ColorTable              = e->bgr;
3258       BlackBgSubst                        = e->BlackBgSubst;
3259       ColorTableRemapping                 = e->ColorTableRemapping;
3260       memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor));
3261       m_txtCache->FullScrColor            = e->DefScreenColor;
3262       m_txtCache->NationalSubset          = SetNational(e->DefaultCharset);
3263       m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset);
3264     }
3265
3266     if (ColorTableRemapping)
3267     {
3268       for (int i = 0; i < 25*40; i++)
3269       {
3270         PageAtrb[i].fg += MapTblFG[ColorTableRemapping - 1];
3271         if (!BlackBgSubst || PageAtrb[i].bg != TXT_ColorBlack || PageAtrb[i].IgnoreAtBlackBgSubst)
3272           PageAtrb[i].bg += MapTblBG[ColorTableRemapping - 1];
3273       }
3274     }
3275
3276     /* determine ?pop/?drcs from MOT */
3277     if (pmot)
3278     {
3279       unsigned char pmot_data[23*40];
3280       g_application.m_pPlayer->LoadPage((m_txtCache->Page & 0xf00) | 0xfe, 0, pmot_data);
3281
3282       unsigned char *p  = pmot_data;      /* start of link data */
3283       int o             = 2 * (((m_txtCache->Page & 0xf0) >> 4) * 10 + (m_txtCache->Page & 0x0f));  /* offset of links for current page */
3284       int opop          = p[o] & 0x07;    /* index of POP link */
3285       int odrcs         = p[o+1] & 0x07;  /* index of DRCS link */
3286       unsigned char obj[3*4*4];           /* types* objects * (triplet,packet,subp,high) */
3287       unsigned char type,ct, tstart = 4*4;
3288       memset(obj,0,sizeof(obj));
3289
3290       if (p[o] & 0x08) /* GPOP data used */
3291       {
3292         if (!m_txtCache->gpop || !(p[18*40] & 0x08)) /* no p27 data or higher prio of MOT link */
3293         {
3294           m_txtCache->gpop = ((p[18*40] << 8) | (p[18*40+1] << 4) | p[18*40+2]) & 0x7ff;
3295           if ((m_txtCache->gpop & 0xff) == 0xff)
3296             m_txtCache->gpop = 0;
3297           else
3298           {
3299             if (m_txtCache->gpop < 0x100)
3300               m_txtCache->gpop += 0x800;
3301             if (!p26Received)
3302             {
3303               ct = 2;
3304               while (ct)
3305               {
3306                 ct--;
3307                 type = (p[18*40+5] >> 2*ct) & 0x03;
3308
3309                 if (type == 0) continue;
3310                   obj[(type-1)*(tstart)+ct*4  ] = 3 * ((p[18*40+7+ct*2] >> 1) & 0x03) + type; //triplet
3311                   obj[(type-1)*(tstart)+ct*4+1] = ((p[18*40+7+ct*2] & 0x08) >> 3) + 1       ; //packet
3312                   obj[(type-1)*(tstart)+ct*4+2] = p[18*40+6+ct*2] & 0x0f                    ; //subp
3313                   obj[(type-1)*(tstart)+ct*4+3] = p[18*40+7+ct*2] & 0x01                    ; //high
3314               }
3315             }
3316           }
3317         }
3318       }
3319       if (opop) /* POP data used */
3320       {
3321         opop = 18*40 + 10*opop;  /* offset to POP link */
3322         if (!m_txtCache->pop || !(p[opop] & 0x08)) /* no p27 data or higher prio of MOT link */
3323         {
3324           m_txtCache->pop = ((p[opop] << 8) | (p[opop+1] << 4) | p[opop+2]) & 0x7ff;
3325           if ((m_txtCache->pop & 0xff) == 0xff)
3326             m_txtCache->pop = 0;
3327           else
3328           {
3329             if (m_txtCache->pop < 0x100)
3330               m_txtCache->pop += 0x800;
3331             if (!p26Received)
3332             {
3333               ct = 2;
3334               while (ct)
3335               {
3336                 ct--;
3337                 type = (p[opop+5] >> 2*ct) & 0x03;
3338
3339                 if (type == 0) continue;
3340                   obj[(type-1)*(tstart)+(ct+2)*4  ] = 3 * ((p[opop+7+ct*2] >> 1) & 0x03) + type; //triplet
3341                   obj[(type-1)*(tstart)+(ct+2)*4+1] = ((p[opop+7+ct*2] & 0x08) >> 3) + 1       ; //packet
3342                   obj[(type-1)*(tstart)+(ct+2)*4+2] = p[opop+6+ct*2]                           ; //subp
3343                   obj[(type-1)*(tstart)+(ct+2)*4+3] = p[opop+7+ct*2] & 0x01                    ; //high
3344               }
3345             }
3346           }
3347         }
3348       }
3349       // eval default objects in correct order
3350       for (int i = 0; i < 12; i++)
3351       {
3352         if (obj[i*4] != 0)
3353         {
3354           APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0;
3355           Eval_NumberedObject(i % 4 > 1 ? m_txtCache->pop : m_txtCache->gpop, obj[i*4+2], obj[i*4+1], obj[i*4], obj[i*4+3], &APx, &APy, &APx0, &APy0, PageChar, PageAtrb);
3356         }
3357       }
3358
3359       if (p[o+1] & 0x08) /* GDRCS data used */
3360       {
3361         if (!m_txtCache->gdrcs || !(p[20*40] & 0x08)) /* no p27 data or higher prio of MOT link */
3362         {
3363           m_txtCache->gdrcs = ((p[20*40] << 8) | (p[20*40+1] << 4) | p[20*40+2]) & 0x7ff;
3364           if ((m_txtCache->gdrcs & 0xff) == 0xff)
3365             m_txtCache->gdrcs = 0;
3366           else if (m_txtCache->gdrcs < 0x100)
3367             m_txtCache->gdrcs += 0x800;
3368         }
3369       }
3370       if (odrcs) /* DRCS data used */
3371       {
3372         odrcs = 20*40 + 4*odrcs;  /* offset to DRCS link */
3373         if (!m_txtCache->drcs || !(p[odrcs] & 0x08)) /* no p27 data or higher prio of MOT link */
3374         {
3375           m_txtCache->drcs = ((p[odrcs] << 8) | (p[odrcs+1] << 4) | p[odrcs+2]) & 0x7ff;
3376           if ((m_txtCache->drcs & 0xff) == 0xff)
3377             m_txtCache->drcs = 0;
3378           else if (m_txtCache->drcs < 0x100)
3379             m_txtCache->drcs += 0x800;
3380         }
3381       }
3382       if (m_txtCache->astCachetable[m_txtCache->gpop][0])
3383         m_txtCache->astCachetable[m_txtCache->gpop][0]->pageinfo.function = FUNC_GPOP;
3384       if (m_txtCache->astCachetable[m_txtCache->pop][0])
3385         m_txtCache->astCachetable[m_txtCache->pop][0]->pageinfo.function = FUNC_POP;
3386       if (m_txtCache->astCachetable[m_txtCache->gdrcs][0])
3387         m_txtCache->astCachetable[m_txtCache->gdrcs][0]->pageinfo.function = FUNC_GDRCS;
3388       if (m_txtCache->astCachetable[m_txtCache->drcs][0])
3389         m_txtCache->astCachetable[m_txtCache->drcs][0]->pageinfo.function = FUNC_DRCS;
3390     } /* if mot */
3391
3392     /* evaluate local extension data from p26 */
3393     if (p26Received)
3394     {
3395       APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0;
3396       Eval_Object(13 * (23-2 + 2), m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage], &APx, &APy, &APx0, &APy0, OBJ_ACTIVE, &PageChar[40], PageChar, PageAtrb); /* 1st triplet p26/0 */
3397     }
3398
3399     {
3400       int o = 0;
3401       for (int r = 0; r < 25; r++)
3402       {
3403         for (int c = 0; c < 40; c++)
3404         {
3405           if (BlackBgSubst && PageAtrb[o].bg == TXT_ColorBlack && !(PageAtrb[o].IgnoreAtBlackBgSubst))
3406           {
3407             if (m_txtCache->FullRowColor[r] == 0x08)
3408               PageAtrb[o].bg = m_txtCache->FullScrColor;
3409             else
3410               PageAtrb[o].bg = m_txtCache->FullRowColor[r];
3411           }
3412           o++;
3413         }
3414       }
3415     }
3416
3417     if (!HintMode)
3418     {
3419       for (int i = 0; i < 25*40; i++)
3420       {
3421         if (PageAtrb[i].concealed) PageAtrb[i].fg = PageAtrb[i].bg;
3422       }
3423     }
3424   } /* is_dec(page) */
3425 }
3426
3427 /* dump interpreted object data to stdout */
3428 /* in: 18 bit object data */
3429 /* out: termination info, >0 if end of object */
3430 void CTeletextDecoder::Eval_Object(int iONr, TextCachedPage_t *pstCachedPage,
3431             unsigned char *pAPx, unsigned char *pAPy,
3432             unsigned char *pAPx0, unsigned char *pAPy0,
3433             tObjType ObjType, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3434 {
3435   int iOData;
3436   int iONr1 = iONr + 1; /* don't terminate after first triplet */
3437   unsigned char drcssubp=0, gdrcssubp=0;
3438   signed char endcol = -1; /* last column to which to extend attribute changes */
3439   TextPageAttr_t attrPassive = { TXT_ColorWhite  , TXT_ColorBlack , C_G0P, 0, 0, 1 ,0, 0, 0, 0, 0, 0, 0, 0x3f}; /* current attribute for passive objects */
3440
3441   do
3442   {
3443     iOData = iTripletNumber2Data(iONr, pstCachedPage, pagedata);  /* get triplet data, next triplet */
3444     if (iOData < 0) /* invalid number, not cached, or hamming error: terminate */
3445       break;
3446
3447     if (endcol < 0)
3448     {
3449       if (ObjType == OBJ_ACTIVE)
3450       {
3451         endcol = 40;
3452       }
3453       else if (ObjType == OBJ_ADAPTIVE) /* search end of line */
3454       {
3455         for (int i = iONr; i <= 506; i++)
3456         {
3457           int iTempOData = iTripletNumber2Data(i, pstCachedPage, pagedata); /* get triplet data, next triplet */
3458           int iAddress = (iTempOData      ) & 0x3f;
3459           int iMode    = (iTempOData >>  6) & 0x1f;
3460           //int iData    = (iTempOData >> 11) & 0x7f;
3461           if (iTempOData < 0 || /* invalid number, not cached, or hamming error: terminate */
3462              (iAddress >= 40  /* new row: row address and */
3463              && (iMode == 0x01 || /* Full Row Color or */
3464                 iMode == 0x04 || /* Set Active Position */
3465                 (iMode >= 0x15 && iMode <= 0x17) || /* Object Definition */
3466                 iMode == 0x17))) /* Object Termination */
3467             break;
3468           if (iAddress < 40 && iMode != 0x06)
3469             endcol = iAddress;
3470         }
3471       }
3472     }
3473     iONr++;
3474   }
3475   while (0 == Eval_Triplet(iOData, pstCachedPage, pAPx, pAPy, pAPx0, pAPy0, &drcssubp, &gdrcssubp, &endcol, &attrPassive, pagedata, PageChar, PageAtrb) || iONr1 == iONr); /* repeat until termination reached */
3476 }
3477
3478 void CTeletextDecoder::Eval_NumberedObject(int p, int s, int packet, int triplet, int high,
3479                  unsigned char *pAPx, unsigned char *pAPy,
3480                  unsigned char *pAPx0, unsigned char *pAPy0, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3481 {
3482   if (!packet || 0 == m_txtCache->astCachetable[p][s])
3483     return;
3484
3485   unsigned char pagedata[23*40];
3486   g_application.m_pPlayer->LoadPage(p, s,pagedata);
3487
3488   int idata = CDVDTeletextTools::deh24(pagedata + 40*(packet-1) + 1 + 3*triplet);
3489   int iONr;
3490
3491   if (idata < 0)  /* hamming error: ignore triplet */
3492     return;
3493   if (high)
3494     iONr = idata >> 9; /* triplet number of odd object data */
3495   else
3496     iONr = idata & 0x1ff; /* triplet number of even object data */
3497   if (iONr <= 506)
3498   {
3499     Eval_Object(iONr, m_txtCache->astCachetable[p][s], pAPx, pAPy, pAPx0, pAPy0, (tObjType)(triplet % 3),pagedata, PageChar, PageAtrb);
3500   }
3501 }
3502
3503 int CTeletextDecoder::Eval_Triplet(int iOData, TextCachedPage_t *pstCachedPage,
3504             unsigned char *pAPx, unsigned char *pAPy,
3505             unsigned char *pAPx0, unsigned char *pAPy0,
3506             unsigned char *drcssubp, unsigned char *gdrcssubp,
3507             signed char *endcol, TextPageAttr_t *attrPassive, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3508 {
3509   int iAddress = (iOData      ) & 0x3f;
3510   int iMode    = (iOData >>  6) & 0x1f;
3511   int iData    = (iOData >> 11) & 0x7f;
3512
3513   if (iAddress < 40) /* column addresses */
3514   {
3515     int offset;  /* offset to PageChar and PageAtrb */
3516
3517     if (iMode != 0x06)
3518       *pAPx = iAddress;  /* new Active Column */
3519     offset = (*pAPy0 + *pAPy) * 40 + *pAPx0 + *pAPx;  /* offset to PageChar and PageAtrb */
3520
3521     switch (iMode)
3522     {
3523     case 0x00:
3524       if (0 == (iData>>5))
3525       {
3526         int newcolor = iData & 0x1f;
3527         if (*endcol < 0) /* passive object */
3528           attrPassive->fg = newcolor;
3529         else if (*endcol == 40) /* active object */
3530         {
3531           TextPageAttr_t *p = &PageAtrb[offset];
3532           int oldcolor = (p)->fg; /* current color (set-after) */
3533           int c = *pAPx0 + *pAPx;  /* current column absolute */
3534           do
3535           {
3536             p->fg = newcolor;
3537             p++;
3538             c++;
3539           } while (c < 40 && p->fg == oldcolor);  /* stop at change by level 1 page */
3540         }
3541         else /* adaptive object */
3542         {
3543           TextPageAttr_t *p = &PageAtrb[offset];
3544           int c = *pAPx;  /* current column relative to object origin */
3545           do
3546           {
3547             p->fg = newcolor;
3548             p++;
3549             c++;
3550           } while (c <= *endcol);
3551         }
3552       }
3553       break;
3554     case 0x01:
3555       if (iData >= 0x20)
3556       {
3557         PageChar[offset] = iData;
3558         if (*endcol < 0) /* passive object */
3559         {
3560           attrPassive->charset = C_G1C; /* FIXME: separated? */
3561           PageAtrb[offset] = *attrPassive;
3562         }
3563         else if (PageAtrb[offset].charset != C_G1S)
3564           PageAtrb[offset].charset = C_G1C; /* FIXME: separated? */
3565       }
3566       break;
3567     case 0x02:
3568     case 0x0b:
3569       PageChar[offset] = iData;
3570       if (*endcol < 0) /* passive object */
3571       {
3572         attrPassive->charset = C_G3;
3573         PageAtrb[offset] = *attrPassive;
3574       }
3575       else
3576         PageAtrb[offset].charset = C_G3;
3577       break;
3578     case 0x03:
3579       if (0 == (iData>>5))
3580       {
3581         int newcolor = iData & 0x1f;
3582         if (*endcol < 0) /* passive object */
3583           attrPassive->bg = newcolor;
3584         else if (*endcol == 40) /* active object */
3585         {
3586           TextPageAttr_t *p = &PageAtrb[offset];
3587           int oldcolor = (p)->bg; /* current color (set-after) */
3588           int c = *pAPx0 + *pAPx;  /* current column absolute */
3589           do
3590           {
3591             p->bg = newcolor;
3592             if (newcolor == TXT_ColorBlack)
3593               p->IgnoreAtBlackBgSubst = 1;
3594             p++;
3595             c++;
3596           } while (c < 40 && p->bg == oldcolor);  /* stop at change by level 1 page */
3597         }
3598         else /* adaptive object */
3599         {
3600           TextPageAttr_t *p = &PageAtrb[offset];
3601           int c = *pAPx;  /* current column relative to object origin */
3602           do
3603           {
3604             p->bg = newcolor;
3605             if (newcolor == TXT_ColorBlack)
3606               p->IgnoreAtBlackBgSubst = 1;
3607             p++;
3608             c++;
3609           } while (c <= *endcol);
3610         }
3611       }
3612       break;
3613     case 0x06:
3614       /* ignore */
3615       break;
3616     case 0x07:
3617       if ((iData & 0x60) != 0) break; // reserved data field
3618       if (*endcol < 0) /* passive object */
3619       {
3620         attrPassive->flashing=iData & 0x1f;
3621         PageAtrb[offset] = *attrPassive;
3622       }
3623       else
3624         PageAtrb[offset].flashing=iData & 0x1f;
3625       break;
3626     case 0x08:
3627       if (*endcol < 0) /* passive object */
3628       {
3629         attrPassive->setG0G2=iData & 0x3f;
3630         PageAtrb[offset] = *attrPassive;
3631       }
3632       else
3633         PageAtrb[offset].setG0G2=iData & 0x3f;
3634       break;
3635     case 0x09:
3636       PageChar[offset] = iData;
3637       if (*endcol < 0) /* passive object */
3638       {
3639         attrPassive->charset = C_G0P; /* FIXME: secondary? */
3640         attrPassive->setX26  = 1;
3641         PageAtrb[offset] = *attrPassive;
3642       }
3643       else
3644       {
3645         PageAtrb[offset].charset = C_G0P; /* FIXME: secondary? */
3646         PageAtrb[offset].setX26  = 1;
3647       }
3648       break;
3649 //    case 0x0b: (see 0x02)
3650     case 0x0c:
3651     {
3652       int conc = (iData & 0x04);
3653       int inv  = (iData & 0x10);
3654       int dw   = (iData & 0x40 ?1:0);
3655       int dh   = (iData & 0x01 ?1:0);
3656       int sep  = (iData & 0x20);
3657       int bw   = (iData & 0x02 ?1:0);
3658       if (*endcol < 0) /* passive object */
3659       {
3660         if (conc)
3661         {
3662           attrPassive->concealed = 1;
3663           attrPassive->fg = attrPassive->bg;
3664         }
3665         attrPassive->inverted = (inv ? 1- attrPassive->inverted : 0);
3666         attrPassive->doubleh = dh;
3667         attrPassive->doublew = dw;
3668         attrPassive->boxwin = bw;
3669         if (bw) attrPassive->IgnoreAtBlackBgSubst = 0;
3670         if (sep)
3671         {
3672           if (attrPassive->charset == C_G1C)
3673             attrPassive->charset = C_G1S;
3674           else
3675             attrPassive->underline = 1;
3676         }
3677         else
3678         {
3679           if (attrPassive->charset == C_G1S)
3680             attrPassive->charset = C_G1C;
3681           else
3682             attrPassive->underline = 0;
3683         }
3684       }
3685       else
3686       {
3687
3688         int c = *pAPx0 + (*endcol == 40 ? *pAPx : 0);  /* current column */
3689         int c1 = offset;
3690         TextPageAttr_t *p = &PageAtrb[offset];
3691         do
3692         {
3693           p->inverted = (inv ? 1- p->inverted : 0);
3694           if (conc)
3695           {
3696             p->concealed = 1;
3697             p->fg = p->bg;
3698           }
3699           if (sep)
3700           {
3701             if (p->charset == C_G1C)
3702               p->charset = C_G1S;
3703             else
3704               p->underline = 1;
3705           }
3706           else
3707           {
3708             if (p->charset == C_G1S)
3709               p->charset = C_G1C;
3710             else
3711               p->underline = 0;
3712           }
3713           p->doublew = dw;
3714           p->doubleh = dh;
3715           p->boxwin = bw;
3716           if (bw) p->IgnoreAtBlackBgSubst = 0;
3717           p++;
3718           c++;
3719           c1++;
3720         } while (c < *endcol);
3721       }
3722       break;
3723     }
3724     case 0x0d:
3725       PageChar[offset] = iData & 0x3f;
3726       if (*endcol < 0) /* passive object */
3727       {
3728         attrPassive->charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp);
3729         PageAtrb[offset] = *attrPassive;
3730       }
3731       else
3732         PageAtrb[offset].charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp);
3733       break;
3734     case 0x0f:
3735       PageChar[offset] = iData;
3736       if (*endcol < 0) /* passive object */
3737       {
3738         attrPassive->charset = C_G2;
3739         PageAtrb[offset] = *attrPassive;
3740       }
3741       else
3742         PageAtrb[offset].charset = C_G2;
3743       break;
3744     default:
3745       if (iMode == 0x10 && iData == 0x2a)
3746         iData = '@';
3747       if (iMode >= 0x10)
3748       {
3749         PageChar[offset] = iData;
3750         if (*endcol < 0) /* passive object */
3751         {
3752           attrPassive->charset = C_G0P;
3753           attrPassive->diacrit = iMode & 0x0f;
3754           attrPassive->setX26  = 1;
3755           PageAtrb[offset] = *attrPassive;
3756         }
3757         else
3758         {
3759           PageAtrb[offset].charset = C_G0P;
3760           PageAtrb[offset].diacrit = iMode & 0x0f;
3761           PageAtrb[offset].setX26  = 1;
3762         }
3763       }
3764       break; /* unsupported or not yet implemented mode: ignore */
3765     } /* switch (iMode) */
3766   }
3767   else /* ================= (iAddress >= 40): row addresses ====================== */
3768   {
3769     switch (iMode)
3770     {
3771     case 0x00:
3772       if (0 == (iData>>5))
3773       {
3774         m_txtCache->FullScrColor = iData & 0x1f;
3775       }
3776       break;
3777     case 0x01:
3778       if (*endcol == 40) /* active object */
3779       {
3780         *pAPy = RowAddress2Row(iAddress);  /* new Active Row */
3781
3782         int color = iData & 0x1f;
3783         int row = *pAPy0 + *pAPy;
3784         int maxrow;
3785
3786         if (row <= 24 && 0 == (iData>>5))
3787           maxrow = row;
3788         else if (3 == (iData>>5))
3789           maxrow = 24;
3790         else
3791           maxrow = -1;
3792         for (; row <= maxrow; row++)
3793           m_txtCache->FullRowColor[row] = color;
3794         *endcol = -1;
3795       }
3796       break;
3797     case 0x04:
3798       *pAPy = RowAddress2Row(iAddress); /* new Active Row */
3799       if (iData < 40)
3800         *pAPx = iData;  /* new Active Column */
3801       *endcol = -1; /* FIXME: check if row changed? */
3802       break;
3803     case 0x07:
3804       if (iAddress == 0x3f)
3805       {
3806         *pAPx = *pAPy = 0; /* new Active Position 0,0 */
3807         if (*endcol == 40) /* active object */
3808         {
3809           int color = iData & 0x1f;
3810           int row = *pAPy0; // + *pAPy;
3811           int maxrow;
3812
3813           if (row <= 24 && 0 == (iData>>5))
3814             maxrow = row;
3815           else if (3 == (iData>>5))
3816             maxrow = 24;
3817           else
3818             maxrow = -1;
3819           for (; row <= maxrow; row++)
3820             m_txtCache->FullRowColor[row] = color;
3821         }
3822         *endcol = -1;
3823       }
3824       break;
3825     case 0x08:
3826     case 0x09:
3827     case 0x0a:
3828     case 0x0b:
3829     case 0x0c:
3830     case 0x0d:
3831     case 0x0e:
3832     case 0x0f:
3833       /* ignore */
3834       break;
3835     case 0x10:
3836       m_txtCache->tAPy = iAddress - 40;
3837       m_txtCache->tAPx = iData;
3838       break;
3839     case 0x11:
3840     case 0x12:
3841     case 0x13:
3842       if (iAddress & 0x10)  /* POP or GPOP */
3843       {
3844         unsigned char APx = 0, APy = 0;
3845         unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy;
3846         int triplet = 3 * ((iData >> 5) & 0x03) + (iMode & 0x03);
3847         int packet = (iAddress & 0x03) + 1;
3848         int subp = iData & 0x0f;
3849         int high = (iData >> 4) & 0x01;
3850
3851
3852         if (APx0 < 40) /* not in side panel */
3853         {
3854           Eval_NumberedObject((iAddress & 0x08) ? m_txtCache->gpop : m_txtCache->pop, subp, packet, triplet, high, &APx, &APy, &APx0, &APy0, PageChar,PageAtrb);
3855         }
3856       }
3857       else if (iAddress & 0x08)  /* local: eval invoked object */
3858       {
3859         unsigned char APx = 0, APy = 0;
3860         unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy;
3861         int descode = ((iAddress & 0x01) << 3) | (iData >> 4);
3862         int triplet = iData & 0x0f;
3863
3864         if (APx0 < 40) /* not in side panel */
3865         {
3866           Eval_Object(13 * 23 + 13 * descode + triplet, pstCachedPage, &APx, &APy, &APx0, &APy0, (tObjType)(triplet % 3), pagedata, PageChar, PageAtrb);
3867         }
3868       }
3869       break;
3870     case 0x15:
3871     case 0x16:
3872     case 0x17:
3873       if (0 == (iAddress & 0x08))  /* Object Definition illegal or only level 3.5 */
3874         break; /* ignore */
3875
3876       m_txtCache->tAPx = m_txtCache->tAPy = 0;
3877       *endcol = -1;
3878       return 0xFF; /* termination by object definition */
3879       break;
3880     case 0x18:
3881       if (0 == (iData & 0x10)) /* DRCS Mode reserved or only level 3.5 */
3882         break; /* ignore */
3883
3884       if (iData & 0x40)
3885         *drcssubp = iData & 0x0f;
3886       else
3887         *gdrcssubp = iData & 0x0f;
3888       break;
3889     case 0x1f:
3890       m_txtCache->tAPx = m_txtCache->tAPy = 0;
3891       *endcol = -1;
3892       return 0x80 | iData; /* explicit termination */
3893       break;
3894     default:
3895       break; /* unsupported or not yet implemented mode: ignore */
3896     } /* switch (iMode) */
3897   } /* (iAddress >= 40): row addresses */
3898
3899   if (iAddress < 40 || iMode != 0x10) /* leave temp. AP-Offset unchanged only immediately after definition */
3900     m_txtCache->tAPx = m_txtCache->tAPy = 0;
3901
3902   return 0; /* normal exit, no termination */
3903 }
3904
3905 /* get object data */
3906 /* in: absolute triplet number (0..506, start at packet 3 byte 1) */
3907 /* in: pointer to cache struct of page data */
3908 /* out: 18 bit triplet data, <0 if invalid number, not cached, or hamming error */
3909 int CTeletextDecoder::iTripletNumber2Data(int iONr, TextCachedPage_t *pstCachedPage, unsigned char* pagedata)
3910 {
3911   if (iONr > 506 || 0 == pstCachedPage)
3912     return -1;
3913
3914   unsigned char *p;
3915   int packet = (iONr / 13) + 3;
3916   int packetoffset = 3 * (iONr % 13);
3917
3918   if (packet <= 23)
3919     p = pagedata + 40*(packet-1) + packetoffset + 1;
3920   else if (packet <= 25)
3921   {
3922     if (0 == pstCachedPage->pageinfo.p24)
3923       return -1;
3924     p = pstCachedPage->pageinfo.p24 + 40*(packet-24) + packetoffset + 1;
3925   }
3926   else
3927   {
3928     int descode = packet - 26;
3929     if (0 == pstCachedPage->pageinfo.ext)
3930       return -1;
3931     if (0 == pstCachedPage->pageinfo.ext->p26[descode])
3932       return -1;
3933     p = pstCachedPage->pageinfo.ext->p26[descode] + packetoffset;  /* first byte (=designation code) is not cached */
3934   }
3935   return CDVDTeletextTools::deh24(p);
3936 }
3937
3938 int CTeletextDecoder::SetNational(unsigned char sec)
3939 {
3940   switch (sec)
3941   {
3942     case 0x08:
3943       return NAT_PL; //polish
3944     case 0x16:
3945     case 0x36:
3946       return NAT_TR; //turkish
3947     case 0x1d:
3948       return NAT_SR; //serbian, croatian, slovenian
3949     case 0x20:
3950       return NAT_SC; // serbian, croatian
3951     case 0x24:
3952       return NAT_RB; // russian, bulgarian
3953     case 0x25:
3954       return NAT_UA; // ukrainian
3955     case 0x22:
3956       return NAT_ET; // estonian
3957     case 0x23:
3958       return NAT_LV; // latvian, lithuanian
3959     case 0x37:
3960       return NAT_GR; // greek
3961     case 0x55:
3962       return NAT_HB; // hebrew
3963     case 0x47:
3964     case 0x57:
3965       return NAT_AR; // arabic
3966   }
3967   return CountryConversionTable[sec & 0x07];
3968 }
3969
3970 int CTeletextDecoder::NextHex(int i) /* return next existing non-decimal page number */
3971 {
3972   int startpage = i;
3973   if (startpage < 0x100)
3974     startpage = 0x100;
3975
3976   do
3977   {
3978     i++;
3979     if (i > 0x8FF)
3980       i = 0x100;
3981     if (i == startpage)
3982       break;
3983   }  while ((m_txtCache->SubPageTable[i] == 0xFF) || IsDec(i));
3984   return i;
3985 }
3986
3987 void CTeletextDecoder::SetColors(unsigned short *pcolormap, int offset, int number)
3988 {
3989   int j = offset; /* index in global color table */
3990
3991   for (int i = 0; i < number; i++)
3992   {
3993     int r = ((pcolormap[i] >> 8) & 0xf) << 4;
3994     int g = ((pcolormap[i] >> 4) & 0xf) << 4;
3995     int b = ((pcolormap[i])      & 0xf) << 4;
3996
3997     if (m_RenderInfo.rd0[j] != r)
3998     {
3999       m_RenderInfo.rd0[j] = r;
4000     }
4001     if (m_RenderInfo.gn0[j] != g)
4002     {
4003       m_RenderInfo.gn0[j] = g;
4004     }
4005     if (m_RenderInfo.bl0[j] != b)
4006     {
4007       m_RenderInfo.bl0[j] = b;
4008     }
4009     j++;
4010   }
4011 }
4012
4013 color_t CTeletextDecoder::GetColorRGB(enumTeletextColor ttc)
4014 {
4015   switch (ttc)
4016   {
4017     case TXT_ColorBlack:       return 0xFF000000;
4018     case TXT_ColorRed:         return 0xFFFC1414;
4019     case TXT_ColorGreen:       return 0xFF24FC24;
4020     case TXT_ColorYellow:      return 0xFFFCC024;
4021     case TXT_ColorBlue:        return 0xFF0000FC;
4022     case TXT_ColorMagenta:     return 0xFFB000FC;
4023     case TXT_ColorCyan:        return 0xFF00FCFC;
4024     case TXT_ColorWhite:       return 0xFFFCFCFC;
4025     case TXT_ColorTransp:      return 0x00000000;
4026     default:                   break;
4027   }
4028
4029  /* Get colors for CLUTs 2+3 */
4030   int index = (int)ttc;
4031   color_t color = (m_RenderInfo.tr0[index] << 24) |
4032                   (m_RenderInfo.bl0[index] << 16) |
4033                   (m_RenderInfo.gn0[index] << 8) |
4034                    m_RenderInfo.rd0[index];
4035   return color;
4036 }
4037