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