diff options
41 files changed, 6164 insertions, 32 deletions
diff --git a/guilib/Key.h b/guilib/Key.h index e1805c9d8b..8c3b1b0977 100644 --- a/guilib/Key.h +++ b/guilib/Key.h @@ -276,6 +276,11 @@ #define ACTION_RELOAD_KEYMAPS 203 // reloads CButtonTranslator's keymaps #define ACTION_GUIPROFILE_BEGIN 204 // start the GUIControlProfiler running +#define ACTION_TELETEXT_RED 215 // Teletext Color buttons to control TopText +#define ACTION_TELETEXT_GREEN 216 // " " " " " " +#define ACTION_TELETEXT_YELLOW 217 // " " " " " " +#define ACTION_TELETEXT_BLUE 218 // " " " " " " + // Window ID defines to make the code a bit more readable #define WINDOW_INVALID 9999 #define WINDOW_HOME 10000 @@ -354,6 +359,8 @@ #define WINDOW_MUSIC_NAV 10502 #define WINDOW_MUSIC_PLAYLIST_EDITOR 10503 +#define WINDOW_DIALOG_OSD_TELETEXT 10600 + //#define WINDOW_VIRTUAL_KEYBOARD 11000 #define WINDOW_DIALOG_SELECT 12000 #define WINDOW_MUSIC_INFO 12001 diff --git a/language/English/strings.xml b/language/English/strings.xml index 27c561155f..b7c239ca19 100644 --- a/language/English/strings.xml +++ b/language/English/strings.xml @@ -2110,6 +2110,8 @@ <string id="23000">Weather plugin</string> <string id="23001">- Plugin settings</string> + <string id="23050">Activate Teletext</string> + <!-- strings 23100 thru 23150 reserved for external player --> <string id="23100">External Player Active</string> <string id="23101">Click OK to terminate the player</string> diff --git a/language/German/strings.xml b/language/German/strings.xml index fafa306039..67c47ad26d 100644 --- a/language/German/strings.xml +++ b/language/German/strings.xml @@ -1954,6 +1954,7 @@ <string id="22043">schwarz/weiß</string> <string id="23000">Wetter-Plugin</string> <string id="23001">- Plugin Einstellungen</string> + <string id="23050">Videotext aktivtieren</string> <string id="29800">Datenbankmodus</string> <string id="29801">QWERTY Tastatur</string> <string id="29999">Automatische Videofilter-Korrektur für Spiele</string> diff --git a/media/Fonts/teletext.ttf b/media/Fonts/teletext.ttf Binary files differnew file mode 100644 index 0000000000..9be6547ed6 --- /dev/null +++ b/media/Fonts/teletext.ttf diff --git a/project/VS2008Express/XBMC.vcproj b/project/VS2008Express/XBMC.vcproj index 1a2fa52997..0e78989faa 100644 --- a/project/VS2008Express/XBMC.vcproj +++ b/project/VS2008Express/XBMC.vcproj @@ -235,7 +235,7 @@ InlineFunctionExpansion="0"
FavorSizeOrSpeed="1"
OmitFramePointers="true"
- AdditionalIncludeDirectories="..\..\xbmc\cores\dvdplayer;..\..\xbmc\win32;..\..\xbmc\cores;..\..\xbmc\;..\..\xbmc\utils;..\..\guilib\;..\..\;..\..\xbmc\lib\libUPnP\Platinum\Source\Devices\MediaRenderer;..\..\xbmc\lib\libUPnP\Platinum\Source\Devices\MediaConnect;..\..\xbmc\lib\libUPnP\Platinum\Source\Devices\MediaServer;..\..\xbmc\lib\libUPnP\Platinum\Source\Core;..\..\xbmc\lib\libUPnP\Platinum\Thirdparty\Neptune\Source\Core;..\..\xbmc\lib\libUPnP\Platinum\Thirdparty\Neptune\Source\System\Win32;..\..\xbmc\cores\dvdplayer\DVDCodecs\Overlay;..\..\xbmc\FileSystem;..\..\xbmc\lib\libRTMP;..\..\xbmc\lib\boost;"..\..\xbmc\lib\libSDL-WIN32\include";..\..\xbmc\lib\libPython\Python\PC;..\..\xbmc\lib\libsamplerate\src;..\..\xbmc\lib"
+ AdditionalIncludeDirectories="..\..\xbmc\cores\dvdplayer;..\..\xbmc\win32;..\..\xbmc\cores;..\..\xbmc\;..\..\xbmc\utils;..\..\guilib\;..\..\;..\..\xbmc\lib\libUPnP\Platinum\Source\Devices\MediaRenderer;..\..\xbmc\lib\libUPnP\Platinum\Source\Devices\MediaConnect;..\..\xbmc\lib\libUPnP\Platinum\Source\Devices\MediaServer;..\..\xbmc\lib\libUPnP\Platinum\Source\Core;..\..\xbmc\lib\libUPnP\Platinum\Thirdparty\Neptune\Source\Core;..\..\xbmc\lib\libUPnP\Platinum\Thirdparty\Neptune\Source\System\Win32;..\..\xbmc\cores\dvdplayer\DVDCodecs\Overlay;..\..\xbmc\FileSystem;..\..\xbmc\lib\libRTMP;..\..\xbmc\lib\boost;"..\..\xbmc\lib\libSDL-WIN32\include";..\..\xbmc\lib\libPython\Python\PC;..\..\xbmc\lib\libsamplerate\src;..\..\xbmc\lib;..\..\guilib\freetype2\include"
PreprocessorDefinitions="_WINDOWS;_MSVC;WIN32;NDEBUG;_WIN32_WINNT=0x0501;WINVER=0x0500;NOMINMAX;_USE_32BIT_TIME_T;HAS_DX"
StringPooling="false"
MinimalRebuild="false"
@@ -714,6 +714,14 @@ >
</File>
<File
+ RelativePath="..\..\xbmc\cores\dvdplayer\DVDPlayerTeletext.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\xbmc\cores\dvdplayer\DVDPlayerTeletext.h"
+ >
+ </File>
+ <File
RelativePath="..\..\xbmc\cores\dvdplayer\DVDPlayerVideo.cpp"
>
</File>
@@ -3158,6 +3166,14 @@ >
</File>
<File
+ RelativePath="..\..\xbmc\utils\Teletext.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\xbmc\utils\Teletext.h"
+ >
+ </File>
+ <File
RelativePath="..\..\xbmc\Temperature.cpp"
>
</File>
@@ -3506,6 +3522,14 @@ >
</File>
<File
+ RelativePath="..\..\xbmc\GUIDialogTeletext.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\xbmc\GUIDialogTeletext.h"
+ >
+ </File>
+ <File
RelativePath="..\..\xbmc\GUIDialogVideoBookmarks.cpp"
>
</File>
@@ -5759,6 +5783,10 @@ >
</File>
<File
+ RelativePath="..\..\xbmc\utils\TeletextDefines.h"
+ >
+ </File>
+ <File
RelativePath="..\..\xbmc\Temperature.h"
>
</File>
diff --git a/skin/PM3.HD/720p/VideoOSD.xml b/skin/PM3.HD/720p/VideoOSD.xml index df57df67a4..83a081450c 100644 --- a/skin/PM3.HD/720p/VideoOSD.xml +++ b/skin/PM3.HD/720p/VideoOSD.xml @@ -116,6 +116,15 @@ <texture>osd_panel.png</texture>
<visible>yes</visible>
</control>
+ <control type="image">
+ <description>Teletext Button</description>
+ <posx>138</posx>
+ <posy>15</posy>
+ <width>74</width>
+ <height>66</height>
+ <texture>osd_button_lyrics.png</texture>
+ <visible>VideoPlayer.HasTeletext + Skin.HasSetting(Player_Show_Teletext_Button)</visible>
+ </control>
<control type="button" id="210">
<description>Skip Bwd</description>
<posx>32</posx>
@@ -223,7 +232,25 @@ <onright>215</onright>
<onup>919</onup>
<ondown>921</ondown>
+ <visible>![VideoPlayer.HasTeletext] | ![Skin.HasSetting(Player_Show_Teletext_Button)]</visible>
</control>
+ <control type="button" id="918">
+ <description>Teletext Button</description>
+ <posx>138</posx>
+ <posy>15</posy>
+ <width>74</width>
+ <height>66</height>
+ <texturefocus>osd_button_lyricsFO.png</texturefocus>
+ <texturenofocus>osd_button_lyrics.png</texturenofocus>
+ <onclick>XBMC.ActivateWindow(Teletext)</onclick>
+ <label>-</label>
+ <font>-</font>
+ <onleft>208</onleft>
+ <onright>215</onright>
+ <onup>919</onup>
+ <ondown>921</ondown>
+ <visible>VideoPlayer.HasTeletext + Skin.HasSetting(Player_Show_Teletext_Button)</visible>
+ </control>
<control type="togglebutton" id="921">
<description>Video Menu</description>
<posx>147</posx>
@@ -280,4 +307,4 @@ </control>
</control>
</controls>
-</window>
\ No newline at end of file +</window>
diff --git a/skin/PM3.HD/720p/custom_SkinSetting_1111.xml b/skin/PM3.HD/720p/custom_SkinSetting_1111.xml index e83fb00e3e..e7e9fe4e1a 100644 --- a/skin/PM3.HD/720p/custom_SkinSetting_1111.xml +++ b/skin/PM3.HD/720p/custom_SkinSetting_1111.xml @@ -144,6 +144,24 @@ <texturenofocus>-</texturenofocus>
<enable>Skin.HasSetting(Use_Startup_Playlist)</enable>
</control>
+ <control type="image" id="112">
+ <width>950</width>
+ <height>2</height>
+ <texture>separator.png</texture>
+ </control>
+ <control type="radiobutton" id="113">
+ <width>950</width>
+ <height>35</height>
+ <font>font12</font>
+ <label>31115</label>
+ <textcolor>grey2</textcolor>
+ <focusedcolor>white</focusedcolor>
+ <texturefocus border="7">list-focus.png</texturefocus>
+ <texturenofocus>-</texturenofocus>
+ <textureradiofocus>radiobutton-focus-2.png</textureradiofocus>
+ <onclick>Skin.ToggleSetting(Player_Show_Teletext_Button)</onclick>
+ <selected>Skin.HasSetting(Player_Show_Teletext_Button)</selected>
+ </control>
</control>
<control type="grouplist" id="9002">
<visible>Skin.String(SkinSettings,2)</visible>
diff --git a/skin/PM3.HD/language/English/strings.xml b/skin/PM3.HD/language/English/strings.xml index 4b844c2282..4bece7fda8 100644 --- a/skin/PM3.HD/language/English/strings.xml +++ b/skin/PM3.HD/language/English/strings.xml @@ -80,6 +80,7 @@ <string id="31112">Show media titles in "Wide icons" view</string>
<string id="31113">Single Image</string>
<string id="31114">Multi Image</string>
+ <string id="31115">Replace Audio Settings Button with Teletext</string>
<!-- Script Settings labels -->
<string id="31200">Custom script options</string>
diff --git a/skin/PM3.HD/language/German/strings.xml b/skin/PM3.HD/language/German/strings.xml index dce1379b8b..6279ac8d64 100644 --- a/skin/PM3.HD/language/German/strings.xml +++ b/skin/PM3.HD/language/German/strings.xml @@ -82,6 +82,7 @@ <string id="31109">Home Screen Hintergründe</string> <string id="31110">button - Aktiviere eigene Hintergründe</string> <string id="31111">Immer Musik Infos anzeigen während der Liedtexte</string> + <string id="31115">Ersetzte Taste für Audio Einstellungen mit Videotext</string> <!-- Script Settings labels --> <string id="31200">Eigene Script Optionen</string> diff --git a/system/Lircmap.xml b/system/Lircmap.xml index 617964da2e..62cbbf394e 100644 --- a/system/Lircmap.xml +++ b/system/Lircmap.xml @@ -48,10 +48,10 @@ <eight>Eight</eight> <nine>Nine</nine> <zero>Zero</zero> - <mytv>Red</mytv> - <mymusic>Green</mymusic> - <mypictures>Yellow</mypictures> - <myvideo>Blue</myvideo> + <red>Red</red> + <green>Green</green> + <yellow>Yellow</yellow> + <blue>Blue</blue> </remote> <remote device="XboxDVDDongle"> @@ -183,9 +183,9 @@ <eight>8</eight> <nine>9</nine> <zero>0</zero> - <mytv>red</mytv> - <mymusic>green</mymusic> - <mypictures>yellow</mypictures> - <myvideo>blue</myvideo> + <red>red</red> + <green>green</green> + <yellow>yellow</yellow> + <blue>blue</blue> </remote> </lircmap> diff --git a/system/keymaps/keyboard.xml b/system/keymaps/keyboard.xml index e73aa0030c..16fcc5cf06 100644 --- a/system/keymaps/keyboard.xml +++ b/system/keymaps/keyboard.xml @@ -163,6 +163,7 @@ <down>BigStepBack</down> <a>AudioDelay</a> <escape>Fullscreen</escape> + <v>XBMC.ActivateWindow(Teletext)</v> </keyboard> </FullscreenVideo> <FullscreenInfo> @@ -489,6 +490,13 @@ <space>Pause</space> </keyboard> </PictureInfo> + <Teletext> + <keyboard> + <backspace>Close</backspace> + <escape>Close</escape> + <v>Close</v> + </keyboard> + </Teletext> <Favourites> <keyboard> <backspace>Close</backspace> diff --git a/system/keymaps/remote.xml b/system/keymaps/remote.xml index c35556de93..5e0f9b95bb 100644 --- a/system/keymaps/remote.xml +++ b/system/keymaps/remote.xml @@ -67,6 +67,10 @@ <mymusic>XBMC.ActivateWindow(MyMusic)</mymusic> <mypictures>XBMC.ActivateWindow(MyPictures)</mypictures> <mytv>XBMC.ActivateWindow(Home)</mytv> + <red>XBMC.ActivateWindow(Home)</red> + <green>XBMC.ActivateWindow(MyVideos)</green> + <yellow>XBMC.ActivateWindow(MyMusic)</yellow> + <blue>XBMC.ActivateWindow(MyPictures)</blue> <zero>Number0</zero> <one>Number1</one> <two>JumpSMS2</two> @@ -138,6 +142,7 @@ <info>Info</info> <enter>AspectRatio</enter> <select>AspectRatio</select> + <teletext>XBMC.ActivateWindow(Teletext)</teletext> </remote> </FullscreenVideo> <FullscreenInfo> @@ -445,6 +450,29 @@ <back>Close</back> </remote> </PictureInfo> + <Teletext> + <remote> + <zero>number0</zero> + <one>number1</one> + <two>number2</two> + <three>number3</three> + <four>number4</four> + <five>number5</five> + <six>number6</six> + <seven>number7</seven> + <eight>number8</eight> + <nine>number9</nine> + <red>Red</red> + <green>Green</green> + <yellow>Yellow</yellow> + <blue>Blue</blue> + <info>Info</info> + <back>Close</back> + <menu>Close</menu> + <start>Close</start> + <teletext>Close</teletext> + </remote> + </Teletext> <Favourites> <remote> <back>Close</back> diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index b34806aeee..897a269f2e 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -210,6 +210,7 @@ #include "GUIDialogAccessPoints.h" #endif #include "GUIDialogFullScreenInfo.h" +#include "GUIDialogTeletext.h" #include "GUIDialogSlider.h" #include "cores/dlgcache.h" @@ -1149,6 +1150,7 @@ HRESULT CApplication::Initialize() #ifdef HAS_DX m_gWindowManager.Add(new CGUIWindowTestPatternDX); // window id = 8 #endif + m_gWindowManager.Add(new CGUIDialogTeletext); // window id = m_gWindowManager.Add(new CGUIWindowSettingsScreenCalibration); // window id = 11 m_gWindowManager.Add(new CGUIWindowSettingsCategory); // window id = 12 slideshow:window id 2007 m_gWindowManager.Add(new CGUIWindowScripts); // window id = 20 @@ -3325,6 +3327,8 @@ HRESULT CApplication::Cleanup() m_gWindowManager.Delete(WINDOW_DIALOG_ACCESS_POINTS); m_gWindowManager.Delete(WINDOW_DIALOG_SLIDER); + m_gWindowManager.Delete(WINDOW_DIALOG_OSD_TELETEXT); + m_gWindowManager.Delete(WINDOW_STARTUP); m_gWindowManager.Delete(WINDOW_LOGIN_SCREEN); m_gWindowManager.Delete(WINDOW_VISUALISATION); diff --git a/xbmc/ButtonTranslator.cpp b/xbmc/ButtonTranslator.cpp index 849190c533..fd5070ca1e 100644 --- a/xbmc/ButtonTranslator.cpp +++ b/xbmc/ButtonTranslator.cpp @@ -44,7 +44,7 @@ typedef struct int action; } ActionMapping; -static const ActionMapping actions[] = +static const ActionMapping actions[] = {{"left" , ACTION_MOVE_LEFT }, {"right" , ACTION_MOVE_RIGHT}, {"up" , ACTION_MOVE_UP }, @@ -185,7 +185,11 @@ static const ActionMapping actions[] = {"filtersms9" , ACTION_FILTER_SMS9}, {"firstpage" , ACTION_FIRST_PAGE}, {"lastpage" , ACTION_LAST_PAGE}, - {"guiprofile" , ACTION_GUIPROFILE_BEGIN}}; + {"guiprofile" , ACTION_GUIPROFILE_BEGIN}, + {"red" , ACTION_TELETEXT_RED}, + {"green" , ACTION_TELETEXT_GREEN}, + {"yellow" , ACTION_TELETEXT_YELLOW}, + {"blue" , ACTION_TELETEXT_BLUE}}; CButtonTranslator& CButtonTranslator::GetInstance() { @@ -821,7 +825,7 @@ bool CButtonTranslator::TranslateActionString(const char *szAction, int &action) if (strAction.Equals("noop")) return true; - + for (unsigned int index=0;index < sizeof(actions)/sizeof(actions[0]);++index) { if (strAction.Equals(actions[index].name)) @@ -880,6 +884,7 @@ int CButtonTranslator::TranslateWindowString(const char *szWindow) else if (strWindow.Equals("videolibrary")) windowID = WINDOW_VIDEO_NAV; else if (strWindow.Equals("videoplaylist")) windowID = WINDOW_VIDEO_PLAYLIST; else if (strWindow.Equals("systeminfo")) windowID = WINDOW_SYSTEM_INFORMATION; + else if (strWindow.Equals("teletext")) windowID = WINDOW_DIALOG_OSD_TELETEXT; else if (strWindow.Equals("guicalibration")) windowID = WINDOW_SCREEN_CALIBRATION; else if (strWindow.Equals("screencalibration")) windowID = WINDOW_SCREEN_CALIBRATION; else if (strWindow.Equals("testpattern")) windowID = WINDOW_TEST_PATTERN; @@ -1047,6 +1052,11 @@ uint32_t CButtonTranslator::TranslateRemoteString(const char *szButton) else if (strButton.Equals("clear")) buttonCode = XINPUT_IR_REMOTE_CLEAR; else if (strButton.Equals("enter")) buttonCode = XINPUT_IR_REMOTE_SELECT; // same as select else if (strButton.Equals("xbox")) buttonCode = XINPUT_IR_REMOTE_DISPLAY; // same as display + else if (strButton.Equals("teletext")) buttonCode = XINPUT_IR_REMOTE_TELETEXT; + else if (strButton.Equals("red")) buttonCode = XINPUT_IR_REMOTE_RED; + else if (strButton.Equals("green")) buttonCode = XINPUT_IR_REMOTE_GREEN; + else if (strButton.Equals("yellow")) buttonCode = XINPUT_IR_REMOTE_YELLOW; + else if (strButton.Equals("blue")) buttonCode = XINPUT_IR_REMOTE_BLUE; else CLog::Log(LOGERROR, "Remote Translator: Can't find button %s", strButton.c_str()); return buttonCode; } diff --git a/xbmc/GUIDialogTeletext.cpp b/xbmc/GUIDialogTeletext.cpp new file mode 100644 index 0000000000..71eaac0273 --- /dev/null +++ b/xbmc/GUIDialogTeletext.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2005-2009 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialogTeletext.h" +#include "utils/log.h" +#include "GUIWindowManager.h" +#include "GUISettings.h" +#include "Application.h" +#include "GUITexture.h" +#include "Texture.h" +#include "Settings.h" + +using namespace std; + +static int teletextFadeAmount = 0; + +CGUIDialogTeletext::CGUIDialogTeletext() + : CGUIDialog(WINDOW_DIALOG_OSD_TELETEXT, "") +{ + m_isDialog = false; + m_pTxtTexture = NULL; +} + +CGUIDialogTeletext::~CGUIDialogTeletext() +{ +} + +bool CGUIDialogTeletext::OnAction(const CAction& action) +{ + if (action.id == ACTION_PREVIOUS_MENU || action.id == ACTION_CLOSE_DIALOG) + { + m_bClose = true; + return true; + } + + if (m_TextDecoder.HandleAction(action)) + return true; + + return CGUIDialog::OnAction(action); +} + +bool CGUIDialogTeletext::OnMessage(CGUIMessage& message) +{ + if (message.GetMessage() == GUI_MSG_WINDOW_INIT) + { + /* Do not open if no teletext is available */ + if (!g_application.m_pPlayer->GetTeletextCache()) + { + Close(); + return true; + } + } + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogTeletext::Render() +{ + // Do not render if we have no texture + if (!m_pTxtTexture) + { + CLog::Log(LOGERROR, "CGUITeletextBox::Render called without texture"); + return; + } + + m_TextDecoder.RenderPage(); + + if (!m_bClose) + { + if (teletextFadeAmount < 100) + teletextFadeAmount = std::min(100, teletextFadeAmount + 5); + } + else + { + if (teletextFadeAmount > 0) + teletextFadeAmount = std::max(0, teletextFadeAmount - 10); + + if (teletextFadeAmount == 0) + Close(); + } + + unsigned char* textureBuffer = (unsigned char*)m_TextDecoder.GetTextureBuffer(); + if (!m_bClose && m_TextDecoder.NeedRendering() && textureBuffer) + { + m_pTxtTexture->Update(m_TextDecoder.GetWidth(), m_TextDecoder.GetHeight(), m_TextDecoder.GetWidth()*4, XB_FMT_B8G8R8A8, textureBuffer, false); + m_TextDecoder.RenderingDone(); + } + + color_t color = ((color_t)(teletextFadeAmount * 2.55f) & 0xff) << 24 | 0xFFFFFF; + CGUITexture::DrawQuad(m_vertCoords, color, m_pTxtTexture); + + CGUIDialog::Render(); +} + +void CGUIDialogTeletext::OnInitWindow() +{ + teletextFadeAmount = 0; + m_bClose = false; + m_windowLoaded = true; + + RESOLUTION res = g_graphicsContext.GetVideoResolution(); + m_vertCoords.SetRect((float)g_settings.m_ResInfo[res].Overscan.left, + (float)g_settings.m_ResInfo[res].Overscan.top, + (float)g_settings.m_ResInfo[res].Overscan.right, + (float)g_settings.m_ResInfo[res].Overscan.bottom); + + if (!m_TextDecoder.InitDecoder()) + { + CLog::Log(LOGERROR, "%s: failed to init teletext decoder", __FUNCTION__); + Close(); + } + + m_pTxtTexture = new CTexture(m_TextDecoder.GetWidth(), m_TextDecoder.GetHeight(), 32, XB_FMT_B8G8R8A8); + if (!m_pTxtTexture) + { + CLog::Log(LOGERROR, "%s: failed to create texture", __FUNCTION__); + Close(); + } + + CGUIDialog::OnInitWindow(); +} + +void CGUIDialogTeletext::OnDeinitWindow(int nextWindowID) +{ + m_windowLoaded = false; + m_TextDecoder.EndDecoder(); + + delete m_pTxtTexture; + m_pTxtTexture = NULL; + + CGUIDialog::OnDeinitWindow(nextWindowID); +} diff --git a/xbmc/GUIDialogTeletext.h b/xbmc/GUIDialogTeletext.h new file mode 100644 index 0000000000..56cd09d7fc --- /dev/null +++ b/xbmc/GUIDialogTeletext.h @@ -0,0 +1,44 @@ +#pragma once +/* + * Copyright (C) 2005-2009 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialog.h" +#include "utils/Teletext.h" + +class CBaseTexture; + +class CGUIDialogTeletext : public CGUIDialog +{ +public: + CGUIDialogTeletext(void); + virtual ~CGUIDialogTeletext(void); + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnAction(const CAction& action); + virtual void Render(); + virtual void OnInitWindow(); + virtual void OnDeinitWindow(int nextWindowID); + +protected: + bool m_bClose; /* Close sendet, needed for fade out */ + CBaseTexture* m_pTxtTexture; /* Texture info class to render to screen */ + CRect m_vertCoords; /* Coordinates of teletext field on screen */ + CTeletextDecoder m_TextDecoder; /* Decoding class for teletext code */ +}; diff --git a/xbmc/GUISettings.cpp b/xbmc/GUISettings.cpp index 9ae1240906..58fed9b1c1 100644 --- a/xbmc/GUISettings.cpp +++ b/xbmc/GUISettings.cpp @@ -484,6 +484,9 @@ void CGUISettings::Initialize() AddFloat(21, "videoplayer.maxspeedadjust", 13504, 5.0f, 0.0f, 0.1f, 10.0f); AddInt(22, "videoplayer.resamplequality", 13505, RESAMPLE_MID, RESAMPLE_LOW, 1, RESAMPLE_REALLYHIGH, SPIN_CONTROL_TEXT); + AddSeparator(23, "videoplayer.sep5"); + AddBool(24, "videoplayer.teletextenabled", 23050, true); + AddCategory(5, "subtitles", 287); AddString(1, "subtitles.font", 288, "arial.ttf", SPIN_CONTROL_TEXT); AddInt(2, "subtitles.height", 289, 28, 16, 2, 74, SPIN_CONTROL_TEXT); // use text as there is a disk based lookup needed diff --git a/xbmc/GUIWindowFullScreen.cpp b/xbmc/GUIWindowFullScreen.cpp index d8fc8a57ce..7c667fb74d 100644 --- a/xbmc/GUIWindowFullScreen.cpp +++ b/xbmc/GUIWindowFullScreen.cpp @@ -502,9 +502,11 @@ bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message) { CGUIWindow::OnMessage(message); + CGUIDialog *pDialog = (CGUIDialog *)m_gWindowManager.GetWindow(WINDOW_DIALOG_OSD_TELETEXT); + if (pDialog) pDialog->Close(true); CGUIDialogSlider *slider = (CGUIDialogSlider *)m_gWindowManager.GetWindow(WINDOW_DIALOG_SLIDER); if (slider) slider->Close(true); - CGUIDialog *pDialog = (CGUIDialog *)m_gWindowManager.GetWindow(WINDOW_OSD); + pDialog = (CGUIDialog *)m_gWindowManager.GetWindow(WINDOW_OSD); if (pDialog) pDialog->Close(true); pDialog = (CGUIDialog *)m_gWindowManager.GetWindow(WINDOW_DIALOG_FULLSCREEN_INFO); if (pDialog) pDialog->Close(true); diff --git a/xbmc/GUIWindowOSD.cpp b/xbmc/GUIWindowOSD.cpp index 3632aa2e0e..cd973e0621 100644 --- a/xbmc/GUIWindowOSD.cpp +++ b/xbmc/GUIWindowOSD.cpp @@ -47,7 +47,8 @@ void CGUIWindowOSD::Render() // check for movement of mouse or a submenu open if (g_Mouse.HasMoved() || m_gWindowManager.IsWindowActive(WINDOW_DIALOG_AUDIO_OSD_SETTINGS) || m_gWindowManager.IsWindowActive(WINDOW_DIALOG_VIDEO_OSD_SETTINGS) - || m_gWindowManager.IsWindowActive(WINDOW_DIALOG_VIDEO_BOOKMARKS)) + || m_gWindowManager.IsWindowActive(WINDOW_DIALOG_VIDEO_BOOKMARKS) + || m_gWindowManager.IsWindowActive(WINDOW_DIALOG_OSD_TELETEXT)) SetAutoClose(3000); } CGUIDialog::Render(); @@ -108,6 +109,8 @@ bool CGUIWindowOSD::OnMessage(CGUIMessage& message) if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true); pDialog = (CGUIDialog *)m_gWindowManager.GetWindow(WINDOW_DIALOG_VIDEO_BOOKMARKS); if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true); + pDialog = (CGUIDialog *)m_gWindowManager.GetWindow(WINDOW_DIALOG_OSD_TELETEXT); + if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true); } break; } diff --git a/xbmc/Makefile b/xbmc/Makefile index df9f455eca..482f14c820 100644 --- a/xbmc/Makefile +++ b/xbmc/Makefile @@ -1,4 +1,4 @@ -INCLUDES+=-I. -Ilinux -Icores -I../guilib -Iutils -IFileSystem +INCLUDES+=-I. -Ilinux -Icores -I../guilib -Iutils -IFileSystem -I/usr/include/freetype2 INCLUDES+=-Ilib/libUPnP/Platinum/ThirdParty/Neptune/Source/Core \ -Ilib/libUPnP/Platinum/Source/Core \ @@ -133,6 +133,7 @@ SRCS=Application.cpp \ GUIDialogSelect.cpp \ GUIDialogSettings.cpp \ GUIDialogSubMenu.cpp \ + GUIDialogTeletext.cpp \ GUIDialogVideoBookmarks.cpp \ GUIDialogVideoSettings.cpp \ GUIDialogVisualisationPresetList.cpp \ diff --git a/xbmc/XBIRRemote.h b/xbmc/XBIRRemote.h index 680760deb4..9aa58ed5c7 100644 --- a/xbmc/XBIRRemote.h +++ b/xbmc/XBIRRemote.h @@ -58,6 +58,13 @@ #define XINPUT_IR_REMOTE_HASH 41 #define XINPUT_IR_REMOTE_CLEAR 249 +// additional keys not defined by xbox remotes but present on generic remotes +#define XINPUT_IR_REMOTE_TELETEXT 250 +#define XINPUT_IR_REMOTE_RED 251 +#define XINPUT_IR_REMOTE_GREEN 252 +#define XINPUT_IR_REMOTE_YELLOW 253 +#define XINPUT_IR_REMOTE_BLUE 254 + typedef struct _XINPUT_IR_REMOTE { BYTE wButtons; diff --git a/xbmc/cores/IPlayer.h b/xbmc/cores/IPlayer.h index 3d6a3f6e40..0a40762d30 100644 --- a/xbmc/cores/IPlayer.h +++ b/xbmc/cores/IPlayer.h @@ -24,6 +24,7 @@ #include "IAudioCallback.h" #include "Key.h" +struct TextCacheStruct_t; class TiXmlElement; class CStreamDetails; @@ -68,7 +69,7 @@ public: virtual bool QueueNextFile(const CFileItem &file) { return false; } virtual void OnNothingToQueueNotify() {} virtual bool CloseFile(){ return true;} - virtual bool IsPlaying() const { return false;} + virtual bool IsPlaying() const { return false;} virtual void Pause() = 0; virtual bool IsPaused() const = 0; virtual bool HasVideo() const = 0; @@ -110,6 +111,9 @@ public: virtual void GetAudioStreamName(int iStream, CStdString &strStreamName){}; virtual void SetAudioStream(int iStream){}; + virtual TextCacheStruct_t* GetTeletextCache() { return NULL; }; + virtual void LoadPage(int p, int sp, unsigned char* buffer) {}; + virtual int GetChapterCount() { return 0; } virtual int GetChapter() { return -1; } virtual void GetChapterName(CStdString& strChapterName) { return; } @@ -138,7 +142,7 @@ public: //Returns true if not playback (paused or stopped beeing filled) virtual bool IsCaching() const {return false;}; //Cache filled in Percent - virtual int GetCacheLevel() const {return -1;}; + virtual int GetCacheLevel() const {return -1;}; virtual bool IsInMenu() const {return false;}; virtual bool HasMenu() { return false; }; @@ -151,6 +155,8 @@ public: virtual CStdString GetPlayerState() { return ""; }; virtual bool SetPlayerState(CStdString state) { return false;}; + virtual CStdString GetPlayingTitle() { return ""; }; + protected: IPlayerCallback& m_callback; }; diff --git a/xbmc/cores/dvdplayer/Codecs/ffmpeg/libavcodec/avcodec.h b/xbmc/cores/dvdplayer/Codecs/ffmpeg/libavcodec/avcodec.h index 906df623ed..4f75e5dac8 100644 --- a/xbmc/cores/dvdplayer/Codecs/ffmpeg/libavcodec/avcodec.h +++ b/xbmc/cores/dvdplayer/Codecs/ffmpeg/libavcodec/avcodec.h @@ -329,6 +329,11 @@ enum CodecID { CODEC_ID_SSA, CODEC_ID_MOV_TEXT, + /* data codecs */ + CODEC_ID_VBI_DATA= 0x17500, + CODEC_ID_VBI_TELETEXT, + CODEC_ID_EBU_TELETEXT, + /* other specific kind of codecs (generally used for attachments) */ CODEC_ID_TTF= 0x18000, diff --git a/xbmc/cores/dvdplayer/Codecs/ffmpeg/libavformat/mpegts.c b/xbmc/cores/dvdplayer/Codecs/ffmpeg/libavformat/mpegts.c index 43ea218b2a..5ab3472210 100644 --- a/xbmc/cores/dvdplayer/Codecs/ffmpeg/libavformat/mpegts.c +++ b/xbmc/cores/dvdplayer/Codecs/ffmpeg/libavformat/mpegts.c @@ -542,7 +542,9 @@ static const StreamType DESC_types[] = { { 0x7a, CODEC_TYPE_AUDIO, CODEC_ID_EAC3 }, /* E-AC-3 descriptor */ { 0x7b, CODEC_TYPE_AUDIO, CODEC_ID_DTS }, { 0x59, CODEC_TYPE_SUBTITLE, CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */ - { 0x56, CODEC_TYPE_DATA, CODEC_ID_NONE }, /* VBI descriptor */ + { 0x45, CODEC_TYPE_DATA, CODEC_ID_VBI_DATA }, /* VBI Data descriptor */ + { 0x46, CODEC_TYPE_DATA, CODEC_ID_VBI_TELETEXT }, /* VBI Teletext descriptor */ + { 0x56, CODEC_TYPE_DATA, CODEC_ID_EBU_TELETEXT }, /* EBU Teletext descriptor */ { 0 }, }; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp index 87ac5fc22b..9a37bac0a0 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp @@ -23,6 +23,11 @@ #include "DVDCodecs/DVDCodecs.h" #include "../../../LangCodeExpander.h" +void CDemuxStreamTeletext::GetStreamInfo(std::string& strInfo) +{ + strInfo = "Teletext Data Stream"; +} + void CDemuxStreamAudio::GetStreamType(std::string& strInfo) { char sInfo[64]; @@ -83,6 +88,19 @@ int CDVDDemux::GetNrOfSubtitleStreams() return iCounter; } +int CDVDDemux::GetNrOfTeletextStreams() +{ + int iCounter = 0; + + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + if (pStream->type == STREAM_TELETEXT) iCounter++; + } + + return iCounter; +} + CDemuxStreamAudio* CDVDDemux::GetStreamFromAudioId(int iAudioIndex) { int counter = -1; @@ -125,6 +143,20 @@ CDemuxStreamSubtitle* CDVDDemux::GetStreamFromSubtitleId(int iSubtitleIndex) return NULL; } +CDemuxStreamTeletext* CDVDDemux::GetStreamFromTeletextId(int iTeletextIndex) +{ + int counter = -1; + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + + if (pStream->type == STREAM_TELETEXT) counter++; + if (iTeletextIndex == counter) + return (CDemuxStreamTeletext*)pStream; + } + return NULL; +} + void CDemuxStream::GetStreamName( std::string& strInfo ) { if( language[0] == 0 ) diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h index 68c6a2379e..c11cc88ee6 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h @@ -52,7 +52,8 @@ enum StreamType STREAM_AUDIO, // audio stream STREAM_VIDEO, // video stream STREAM_DATA, // data stream - STREAM_SUBTITLE // subtitle stream + STREAM_SUBTITLE,// subtitle stream + STREAM_TELETEXT // Teletext data stream }; enum StreamSource { @@ -175,6 +176,16 @@ public: int identifier; }; +class CDemuxStreamTeletext : public CDemuxStream +{ +public: + CDemuxStreamTeletext() : CDemuxStream() + { + type = STREAM_TELETEXT; + } + virtual void GetStreamInfo(std::string& strInfo); +}; + typedef struct DemuxPacket { BYTE* pData; // data @@ -285,6 +296,11 @@ public: int GetNrOfSubtitleStreams(); /* + * return nr of teletext streams, 0 if none + */ + int GetNrOfTeletextStreams(); + + /* * return the audio stream, or NULL if it does not exist */ CDemuxStreamAudio* GetStreamFromAudioId(int iAudioIndex); @@ -300,6 +316,11 @@ public: CDemuxStreamSubtitle* GetStreamFromSubtitleId(int iSubtitleIndex); /* + * return the teletext stream, or NULL if it does not exist + */ + CDemuxStreamTeletext* GetStreamFromTeletextId(int iTeletextIndex); + + /* * return a user-presentable codec name of the given stream */ virtual void GetStreamCodecName(int iStreamId, CStdString &strName) {}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp index 517df2405e..03da593fee 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp @@ -42,6 +42,7 @@ #include "DVDClock.h" // for DVD_TIME_BASE #include "utils/Win32Exception.h" #include "AdvancedSettings.h" +#include "GUISettings.h" #include "FileSystem/File.h" #include "utils/log.h" #include "Thread.h" @@ -998,9 +999,21 @@ void CDVDDemuxFFmpeg::AddStream(int iId) } case CODEC_TYPE_DATA: { - m_streams[iId] = new CDemuxStream(); - m_streams[iId]->type = STREAM_DATA; - break; +#if (! defined USE_EXTERNAL_FFMPEG) + if (pStream->codec->codec_id == CODEC_ID_EBU_TELETEXT && g_guiSettings.GetBool("videoplayer.teletextenabled")) + { + CDemuxStreamTeletext* st = new CDemuxStreamTeletext(); + m_streams[iId] = st; + m_streams[iId]->type = STREAM_TELETEXT; + break; + } + else +#endif + { + m_streams[iId] = new CDemuxStream(); + m_streams[iId]->type = STREAM_DATA; + break; + } } case CODEC_TYPE_SUBTITLE: { diff --git a/xbmc/cores/dvdplayer/DVDMessageQueue.cpp b/xbmc/cores/dvdplayer/DVDMessageQueue.cpp index d429a5b41e..4778042e19 100644 --- a/xbmc/cores/dvdplayer/DVDMessageQueue.cpp +++ b/xbmc/cores/dvdplayer/DVDMessageQueue.cpp @@ -224,7 +224,7 @@ MsgQueueReturnCode CDVDMessageQueue::Get(CDVDMsg** pMsg, unsigned int iTimeoutIn m_iDataSize -= pMsgDemuxerPacket->GetPacketSize(); if(m_iDataSize == 0) { - if(!m_bEmptied) + if(!m_bEmptied && m_owner != "teletext") // Prevent log flooding CLog::Log(LOGWARNING, "CDVDMessageQueue(%s)::Get - retrieved last data packet of queue", m_owner.c_str()); m_bEmptied = true; } diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp index d68a8a6082..a34d75b17d 100644 --- a/xbmc/cores/dvdplayer/DVDPlayer.cpp +++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp @@ -143,6 +143,8 @@ int CSelectionStreams::IndexOf(StreamType type, CDVDPlayer& p) return IndexOf(type, p.m_CurrentVideo.source, p.m_CurrentVideo.id); else if(type == STREAM_SUBTITLE) return IndexOf(type, p.m_CurrentSubtitle.source, p.m_CurrentSubtitle.id); + else if(type == STREAM_TELETEXT) + return IndexOf(type, p.m_CurrentTeletext.source, p.m_CurrentTeletext.id); return -1; } @@ -255,10 +257,12 @@ CDVDPlayer::CDVDPlayer(IPlayerCallback& callback) m_CurrentAudio(STREAM_AUDIO), m_CurrentVideo(STREAM_VIDEO), m_CurrentSubtitle(STREAM_SUBTITLE), + m_CurrentTeletext(STREAM_TELETEXT), m_messenger("player"), m_dvdPlayerVideo(&m_clock, &m_overlayContainer, m_messenger), m_dvdPlayerAudio(&m_clock), - m_dvdPlayerSubtitle(&m_overlayContainer) + m_dvdPlayerSubtitle(&m_overlayContainer), + m_dvdPlayerTeletext() { m_pDemuxer = NULL; m_pSubtitleDemuxer = NULL; @@ -399,6 +403,7 @@ void CDVDPlayer::OnStartup() m_CurrentVideo.Clear(); m_CurrentAudio.Clear(); m_CurrentSubtitle.Clear(); + m_CurrentTeletext.Clear(); m_messenger.Init(); @@ -577,6 +582,17 @@ void CDVDPlayer::OpenDefaultStreams() else m_dvdPlayerVideo.EnableSubtitle(false); + // open teletext data stream + count = m_SelectionStreams.Count(STREAM_TELETEXT); + valid = false; + for(int i = 0;i<count && !valid;i++) + { + SelectionStream& s = m_SelectionStreams.Get(STREAM_TELETEXT, i); + if(OpenTeletextStream(s.id, s.source)) + valid = true; + } + if(!valid) + CloseTeletextStream(true); } bool CDVDPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream) @@ -729,6 +745,9 @@ bool CDVDPlayer::IsBetterStream(CCurrentStream& current, CDemuxStream* stream) if(current.type == STREAM_SUBTITLE) return false; + if(current.type == STREAM_TELETEXT) + return false; + if(current.id < 0) return true; } @@ -978,9 +997,12 @@ void CDVDPlayer::Process() m_dvdPlayerVideo.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF)); if(m_CurrentSubtitle.inited) m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF)); + if(m_CurrentTeletext.inited) + m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF)); m_CurrentAudio.inited = false; m_CurrentVideo.inited = false; m_CurrentSubtitle.inited = false; + m_CurrentTeletext.inited = false; // if we are caching, start playing it again if (m_caching && !m_bAbortRequest) @@ -1018,6 +1040,7 @@ void CDVDPlayer::Process() if (!IsValidStream(m_CurrentAudio)) CloseAudioStream(true); if (!IsValidStream(m_CurrentVideo)) CloseVideoStream(true); if (!IsValidStream(m_CurrentSubtitle)) CloseSubtitleStream(true); + if (!IsValidStream(m_CurrentTeletext)) CloseTeletextStream(true); } // check if there is any better stream to use (normally for dvd's) @@ -1026,6 +1049,7 @@ void CDVDPlayer::Process() // Do not reopen non-video streams if we're in video-only mode if (IsBetterStream(m_CurrentAudio, pStream)) OpenAudioStream(pStream->iId, pStream->source); if (IsBetterStream(m_CurrentSubtitle, pStream)) OpenSubtitleStream(pStream->iId, pStream->source); + if (IsBetterStream(m_CurrentTeletext, pStream)) OpenTeletextStream(pStream->iId, pStream->source); } if (IsBetterStream(m_CurrentVideo, pStream)) OpenVideoStream(pStream->iId, pStream->source); @@ -1057,6 +1081,8 @@ void CDVDPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket) ProcessVideoData(pStream, pPacket); else if (pPacket->iStreamId == m_CurrentSubtitle.id && pStream->source == m_CurrentSubtitle.source && pStream->type == STREAM_SUBTITLE) ProcessSubData(pStream, pPacket); + else if (pPacket->iStreamId == m_CurrentTeletext.id && pStream->source == m_CurrentTeletext.source && pStream->type == STREAM_TELETEXT) + ProcessTeletextData(pStream, pPacket); else { pStream->SetDiscard(AVDISCARD_ALL); @@ -1182,6 +1208,39 @@ void CDVDPlayer::ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket) m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL); } +void CDVDPlayer::ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket) +{ + if (m_CurrentTeletext.stream != (void*)pStream) + { + /* check so that dmuxer hints or extra data hasn't changed */ + /* if they have, reopen stream */ + + if (m_CurrentTeletext.hint != CDVDStreamInfo(*pStream, true)) + { + // we don't actually have to close audiostream here first, as + // we could send it as a stream message. only problem + // is how to notify player if a stream change failed. + CloseTeletextStream( true ); + OpenTeletextStream( pPacket->iStreamId, pStream->source ); + } + + m_CurrentTeletext.stream = (void*)pStream; + } + if(pPacket->dts != DVD_NOPTS_VALUE) + m_CurrentTeletext.dts = pPacket->dts; + else if(pPacket->pts != DVD_NOPTS_VALUE) + m_CurrentTeletext.dts = pPacket->pts; + + bool drop = false; + if (CheckPlayerInit(m_CurrentTeletext, DVDPLAYER_TELETEXT)) + drop = true; + + if (CheckSceneSkip(m_CurrentTeletext)) + drop = true; + + m_dvdPlayerTeletext.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop)); +} + void CDVDPlayer::HandlePlaySpeed() { if(GetPlaySpeed() != DVD_PLAYSPEED_NORMAL && GetPlaySpeed() != DVD_PLAYSPEED_PAUSE) @@ -1252,6 +1311,8 @@ bool CDVDPlayer::CheckPlayerInit(CCurrentStream& current, unsigned int source) m_CurrentVideo.startpts = current.dts; if(m_CurrentSubtitle.startpts != DVD_NOPTS_VALUE) m_CurrentSubtitle.startpts = current.dts; + if(m_CurrentTeletext.startpts != DVD_NOPTS_VALUE) + m_CurrentTeletext.startpts = current.dts; } } @@ -1381,6 +1442,7 @@ void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket) m_CurrentAudio.inited = false; m_CurrentVideo.inited = false; m_CurrentSubtitle.inited = false; + m_CurrentTeletext.inited = false; } /* stream jump forward */ @@ -1394,6 +1456,7 @@ void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket) m_CurrentAudio.inited = false; m_CurrentVideo.inited = false; m_CurrentSubtitle.inited = false; + m_CurrentTeletext.inited = false; } } @@ -1525,7 +1588,8 @@ void CDVDPlayer::SyncronizePlayers(DWORD sources, double pts) /* if we are awaiting a start sync, we can't sync here or we could deadlock */ if(m_CurrentAudio.startsync || m_CurrentVideo.startsync - || m_CurrentSubtitle.startsync) + || m_CurrentSubtitle.startsync + || m_CurrentTeletext.startsync) { CLog::Log(LOGDEBUG, "%s - can't sync since we are already awaiting a sync", __FUNCTION__); return; @@ -1571,6 +1635,8 @@ void CDVDPlayer::SendPlayerMessage(CDVDMsg* pMsg, unsigned int target) m_dvdPlayerVideo.SendMessage(pMsg); if(target == DVDPLAYER_SUBTITLE) m_dvdPlayerSubtitle.SendMessage(pMsg); + if(target == DVDPLAYER_TELETEXT) + m_dvdPlayerTeletext.SendMessage(pMsg); } void CDVDPlayer::OnExit() @@ -1613,6 +1679,11 @@ void CDVDPlayer::OnExit() CLog::Log(LOGNOTICE, "DVDPlayer: closing video stream"); CloseSubtitleStream(!m_bAbortRequest); } + if (m_CurrentTeletext.id >= 0) + { + CLog::Log(LOGNOTICE, "DVDPlayer: closing teletext stream"); + CloseTeletextStream(!m_bAbortRequest); + } // destroy the demuxer if (m_pDemuxer) { @@ -2237,6 +2308,22 @@ void CDVDPlayer::SetAudioStream(int iStream) SyncronizeDemuxer(100); } +TextCacheStruct_t* CDVDPlayer::GetTeletextCache() +{ + if (m_CurrentTeletext.id < 0) + return 0; + + return m_dvdPlayerTeletext.GetTeletextCache(); +} + +void CDVDPlayer::LoadPage(int p, int sp, unsigned char* buffer) +{ + if (m_CurrentTeletext.id < 0) + return; + + return m_dvdPlayerTeletext.LoadPage(p, sp, buffer); +} + void CDVDPlayer::SeekTime(__int64 iTime) { m_messenger.Put(new CDVDMsgPlayerSeek((int)iTime, true, true, true)); @@ -2489,6 +2576,52 @@ bool CDVDPlayer::OpenSubtitleStream(int iStream, int source) return true; } +bool CDVDPlayer::OpenTeletextStream(int iStream, int source) +{ + if (!m_pDemuxer) + return false; + + CDemuxStream* pStream = m_pDemuxer->GetStream(iStream); + if(!pStream || pStream->disabled) + return false; + + CDVDStreamInfo hint(*pStream, true); + + if (!m_dvdPlayerTeletext.CheckStream(hint)) + return false; + + CLog::Log(LOGNOTICE, "Opening teletext stream: %i source: %i", iStream, source); + + if(m_CurrentTeletext.id < 0 + || m_CurrentTeletext.hint != hint) + { + if(m_CurrentTeletext.id >= 0) + { + CLog::Log(LOGDEBUG, " - teletext codecs hints have changed, must close previous stream"); + CloseTeletextStream(true); + } + + if (!m_dvdPlayerTeletext.OpenStream(hint)) + { + /* mark stream as disabled, to disallaw further attempts*/ + CLog::Log(LOGWARNING, "%s - Unsupported teletext stream %d. Stream disabled.", __FUNCTION__, iStream); + pStream->disabled = true; + pStream->SetDiscard(AVDISCARD_ALL); + return false; + } + } + else + m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_FLUSH)); + + /* store information about stream */ + m_CurrentTeletext.id = iStream; + m_CurrentTeletext.source = source; + m_CurrentTeletext.hint = hint; + m_CurrentTeletext.stream = (void*)pStream; + + return true; +} + bool CDVDPlayer::CloseAudioStream(bool bWaitForBuffers) { if (m_CurrentAudio.id < 0) @@ -2528,6 +2661,19 @@ bool CDVDPlayer::CloseSubtitleStream(bool bKeepOverlays) return true; } +bool CDVDPlayer::CloseTeletextStream(bool bWaitForBuffers) +{ + if (m_CurrentTeletext.id < 0) + return false; + + CLog::Log(LOGNOTICE, "Closing teletext stream"); + + m_dvdPlayerTeletext.CloseStream(bWaitForBuffers); + + m_CurrentTeletext.Clear(); + return true; +} + void CDVDPlayer::FlushBuffers(bool queued) { if(queued) @@ -2536,6 +2682,7 @@ void CDVDPlayer::FlushBuffers(bool queued) m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_FLUSH)); m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP)); m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_FLUSH)); + m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_FLUSH)); SyncronizePlayers(SYNCSOURCE_ALL); } else @@ -2543,6 +2690,7 @@ void CDVDPlayer::FlushBuffers(bool queued) m_dvdPlayerAudio.Flush(); m_dvdPlayerVideo.Flush(); m_dvdPlayerSubtitle.Flush(); + m_dvdPlayerTeletext.Flush(); // clear subtitle and menu overlays m_overlayContainer.Clear(); @@ -2550,6 +2698,7 @@ void CDVDPlayer::FlushBuffers(bool queued) m_CurrentAudio.inited = false; m_CurrentVideo.inited = false; m_CurrentSubtitle.inited = false; + m_CurrentTeletext.inited = false; } // since we call ffmpeg functions to decode, this is being called in the same thread as ::Process() is @@ -3266,6 +3415,16 @@ bool CDVDPlayer::GetStreamDetails(CStreamDetails &details) return false; } +CStdString CDVDPlayer::GetPlayingTitle() +{ + /* Currently we support only Title Name from Teletext line 30 */ + TextCacheStruct_t* ttcache = m_dvdPlayerTeletext.GetTeletextCache(); + if (ttcache && !ttcache->line30.empty()) + return ttcache->line30; + + return ""; +} + CDVDPlayer::CPlayerSeek::CPlayerSeek(CDVDPlayer* player) : m_player(*player) { diff --git a/xbmc/cores/dvdplayer/DVDPlayer.h b/xbmc/cores/dvdplayer/DVDPlayer.h index 7d732bc4ed..d0700ee7d1 100644 --- a/xbmc/cores/dvdplayer/DVDPlayer.h +++ b/xbmc/cores/dvdplayer/DVDPlayer.h @@ -31,6 +31,7 @@ #include "DVDPlayerAudio.h" #include "DVDPlayerVideo.h" #include "DVDPlayerSubtitle.h" +#include "DVDPlayerTeletext.h" //#include "DVDChapterReader.h" #include "DVDSubtitles/DVDFactorySubtitle.h" @@ -127,6 +128,7 @@ public: #define DVDPLAYER_AUDIO 1 #define DVDPLAYER_VIDEO 2 #define DVDPLAYER_SUBTITLE 3 +#define DVDPLAYER_TELETEXT 4 class CDVDPlayer : public IPlayer, public CThread, public IDVDPlayer { @@ -178,6 +180,9 @@ public: virtual void GetAudioStreamName(int iStream, CStdString &strStreamName); virtual void SetAudioStream(int iStream); + virtual TextCacheStruct_t* GetTeletextCache(); + virtual void LoadPage(int p, int sp, unsigned char* buffer); + virtual int GetChapterCount(); virtual int GetChapter(); virtual void GetChapterName(CStdString& strChapterName); @@ -202,6 +207,8 @@ public: virtual CStdString GetPlayerState(); virtual bool SetPlayerState(CStdString state); + + virtual CStdString GetPlayingTitle(); virtual bool IsCaching() const { return m_caching; } virtual int GetCacheLevel() const ; @@ -219,14 +226,17 @@ protected: bool OpenAudioStream(int iStream, int source); bool OpenVideoStream(int iStream, int source); bool OpenSubtitleStream(int iStream, int source); + bool OpenTeletextStream(int iStream, int source); bool CloseAudioStream(bool bWaitForBuffers); bool CloseVideoStream(bool bWaitForBuffers); bool CloseSubtitleStream(bool bKeepOverlays); + bool CloseTeletextStream(bool bWaitForBuffers); void ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket); void ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket); void ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket); void ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket); + void ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket); bool AddSubtitleFile(const std::string& filename); /** @@ -275,6 +285,7 @@ protected: CCurrentStream m_CurrentAudio; CCurrentStream m_CurrentVideo; CCurrentStream m_CurrentSubtitle; + CCurrentStream m_CurrentTeletext; CSelectionStreams m_SelectionStreams; @@ -293,6 +304,7 @@ protected: CDVDPlayerVideo m_dvdPlayerVideo; // video part CDVDPlayerAudio m_dvdPlayerAudio; // audio part CDVDPlayerSubtitle m_dvdPlayerSubtitle; // subtitle part + CDVDTeletextData m_dvdPlayerTeletext; // teletext part CDVDClock m_clock; // master clock CDVDOverlayContainer m_overlayContainer; diff --git a/xbmc/cores/dvdplayer/DVDPlayerTeletext.cpp b/xbmc/cores/dvdplayer/DVDPlayerTeletext.cpp new file mode 100644 index 0000000000..75c4d46a1a --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDPlayerTeletext.cpp @@ -0,0 +1,781 @@ +/* + * Copyright (C) 2005-2009 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Settings.h" +#include "DVDPlayer.h" +#include "DVDStreamInfo.h" +#include "DVDPlayerTeletext.h" +#include "Application.h" +#include "DVDCodecs/DVDCodecs.h" +#include "utils/log.h" + +using namespace std; + +const uint8_t rev_lut[32] = +{ + 0x00,0x08,0x04,0x0c, /* upper nibble */ + 0x02,0x0a,0x06,0x0e, + 0x01,0x09,0x05,0x0d, + 0x03,0x0b,0x07,0x0f, + 0x00,0x80,0x40,0xc0, /* lower nibble */ + 0x20,0xa0,0x60,0xe0, + 0x10,0x90,0x50,0xd0, + 0x30,0xb0,0x70,0xf0 +}; + +void CDVDTeletextTools::NextDec(int *i) /* skip to next decimal */ +{ + (*i)++; + + if ((*i & 0x0F) > 0x09) + *i += 0x06; + + if ((*i & 0xF0) > 0x90) + *i += 0x60; + + if (*i > 0x899) + *i = 0x100; +} + +void CDVDTeletextTools::PrevDec(int *i) /* counting down */ +{ + (*i)--; + + if ((*i & 0x0F) > 0x09) + *i -= 0x06; + + if ((*i & 0xF0) > 0x90) + *i -= 0x60; + + if (*i < 0x100) + *i = 0x899; +} + +/* print hex-number into string, s points to last digit, caller has to provide enough space, no termination */ +void CDVDTeletextTools::Hex2Str(char *s, unsigned int n) +{ + do { + char c = (n & 0xF); + *s-- = number2char(c); + n >>= 4; + } while (n); +} + +signed int CDVDTeletextTools::deh24(unsigned char *p) +{ + int e = hamm24par[0][p[0]] + ^ hamm24par[1][p[1]] + ^ hamm24par[2][p[2]]; + + int x = hamm24val[p[0]] + + (p[1] & 127) * 16 + + (p[2] & 127) * 2048; + + return (x ^ hamm24cor[e]) | hamm24err[e]; +} + + +CDVDTeletextData::CDVDTeletextData() +: CThread() +, m_messageQueue("teletext") +{ + m_speed = DVD_PLAYSPEED_NORMAL; + + m_messageQueue.SetMaxDataSize(40 * 256 * 1024); + + /* Initialize Data structures */ + memset(&m_TXTCache.astCachetable, 0, sizeof(m_TXTCache.astCachetable)); + memset(&m_TXTCache.astP29, 0, sizeof(m_TXTCache.astP29)); + ResetTeletextCache(); +} + +CDVDTeletextData::~CDVDTeletextData() +{ + StopThread(); + ResetTeletextCache(); +} + +bool CDVDTeletextData::CheckStream(CDVDStreamInfo &hints) +{ +#if (! defined USE_EXTERNAL_FFMPEG) + if (hints.codec == CODEC_ID_EBU_TELETEXT) + return true; +#endif + + return false; +} + +bool CDVDTeletextData::OpenStream(CDVDStreamInfo &hints) +{ + m_messageQueue.Init(); + +#if (! defined USE_EXTERNAL_FFMPEG) + if (hints.codec == CODEC_ID_EBU_TELETEXT) + { + CLog::Log(LOGNOTICE, "Creating teletext data thread"); + Create(); + return true; + } +#endif + + return false; +} + +void CDVDTeletextData::CloseStream(bool bWaitForBuffers) +{ + // wait until buffers are empty + if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty(); + + m_messageQueue.Abort(); + + // wait for decode_video thread to end + CLog::Log(LOGNOTICE, "waiting for teletext data thread to exit"); + + StopThread(); // will set this->m_bStop to true + + m_messageQueue.End(); + ResetTeletextCache(); +} + + +void CDVDTeletextData::ResetTeletextCache() +{ + CSingleLock lock(m_critSection); + + /* Reset Data structures */ + for (int i = 0; i < 0x900; i++) + { + for (int j = 0; j < 0x80; j++) + { + if (m_TXTCache.astCachetable[i][j]) + { + TextPageinfo_t *p = &(m_TXTCache.astCachetable[i][j]->pageinfo); + if (p->p24) + free(p->p24); + + if (p->ext) + { + if (p->ext->p27) + free(p->ext->p27); + + for (int d26 = 0; d26 < 16; d26++) + { + if (p->ext->p26[d26]) + free(p->ext->p26[d26]); + } + free(p->ext); + } + delete m_TXTCache.astCachetable[i][j]; + m_TXTCache.astCachetable[i][j] = 0; + } + } + } + + for (int i = 0; i < 9; i++) + { + if (m_TXTCache.astP29[i]) + { + if (m_TXTCache.astP29[i]->p27) + free(m_TXTCache.astP29[i]->p27); + + for (int d26 = 0; d26 < 16; d26++) + { + if (m_TXTCache.astP29[i]->p26[d26]) + free(m_TXTCache.astP29[i]->p26[d26]); + } + free(m_TXTCache.astP29[i]); + m_TXTCache.astP29[i] = 0; + } + m_TXTCache.CurrentPage[i] = -1; + m_TXTCache.CurrentSubPage[i] = -1; + } + + memset(&m_TXTCache.SubPageTable, 0xFF, sizeof(m_TXTCache.SubPageTable)); + memset(&m_TXTCache.astP29, 0, sizeof(m_TXTCache.astP29)); + memset(&m_TXTCache.BasicTop, 0, sizeof(m_TXTCache.BasicTop)); + memset(&m_TXTCache.ADIPTable, 0, sizeof(m_TXTCache.ADIPTable)); + memset(&m_TXTCache.FlofPages, 0, sizeof(m_TXTCache.FlofPages)); + memset(&m_TXTCache.SubtitlePages, 0, sizeof(m_TXTCache.SubtitlePages)); + memset(&m_TXTCache.astCachetable, 0, sizeof(m_TXTCache.astCachetable)); + memset(&m_TXTCache.TimeString, 0x20, 8); + + m_TXTCache.NationalSubset = NAT_DEFAULT;/* default */ + m_TXTCache.NationalSubsetSecondary = NAT_DEFAULT; + m_TXTCache.ZapSubpageManual = false; + m_TXTCache.PageUpdate = false; + m_TXTCache.ADIP_PgMax = -1; + m_TXTCache.BTTok = false; + m_TXTCache.CachedPages = 0; + m_TXTCache.PageReceiving = -1; + m_TXTCache.Page = 0x100; + m_TXTCache.SubPage = m_TXTCache.SubPageTable[m_TXTCache.Page]; + m_TXTCache.line30 = ""; + if (m_TXTCache.SubPage == 0xff) + m_TXTCache.SubPage = 0; +} + +void CDVDTeletextData::OnStartup() +{ + CThread::SetName("CDVDTeletextData"); +} + +void CDVDTeletextData::Process() +{ + int b1, b2, b3, b4; + int packet_number; + TextPageinfo_t *pageinfo_thread; + unsigned char vtxt_row[42]; + unsigned char pagedata[9][23*40]; + unsigned char magazine = 0xff; + int doupdate = 0; + + CLog::Log(LOGNOTICE, "running thread: CDVDTeletextData"); + + while (!m_bStop) + { + CDVDMsg* pMsg; + int iPriority = (m_speed == DVD_PLAYSPEED_PAUSE) ? 1 : 0; + MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, 2000, iPriority); + + if (ret == MSGQ_TIMEOUT) + { + /* Timeout for Teletext is not a bad thing, so we continue without error */ + continue; + } + + if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT) + { + CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true (%i)", ret); + break; + } + + if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) + { + CSingleLock lock(m_critSection); + + DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket(); + bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop(); + uint8_t *Datai = pPacket->pData; + int rows = (pPacket->iSize - 1) / 46; + + /* Is it a ITU-R System B Teletext stream in acc. to EN 300 472 */ + if (Datai[0] >= 0x10 && Datai[0] <= 0x1F) /* Check we have a valid data identifier */ + { + /* Go thru the pages stored inside this frame */ + for (int row=0; row < rows; row++) + { + uint8_t *vtx_rowbyte = &Datai[(row*46)+1]; + int err = 0; + + /* Check for valid data_unit_id */ + if ((vtx_rowbyte[0] == 0x02 || vtx_rowbyte[0] == 0x03) && (vtx_rowbyte[1] == 0x2C)) + { + /* clear rowbuffer */ + /* convert row from lsb to msb (begin with magazin number) */ + for (int i = 4; i < 46; i++) + { + uint8_t upper = (vtx_rowbyte[i] >> 4) & 0xf; + uint8_t lower = vtx_rowbyte[i] & 0xf; + vtxt_row[i-4] = (rev_lut[upper]) | (rev_lut[lower+16]); + } + + /* get packet number */ + b1 = dehamming[vtxt_row[0]]; + b2 = dehamming[vtxt_row[1]]; + + if (b1 == 0xFF || b2 == 0xFF) + continue; + + b1 &= 8; + + /* get packet and magazine number */ + packet_number = b1>>3 | b2<<1; + magazine = dehamming[vtxt_row[0]] & 7; + if (!magazine) magazine = 8; + + if (packet_number == 0 && m_TXTCache.CurrentPage[magazine] != -1 && m_TXTCache.CurrentSubPage[magazine] != -1) + SavePage(m_TXTCache.CurrentPage[magazine], m_TXTCache.CurrentSubPage[magazine], pagedata[magazine]); + + /* analyze row */ + if (packet_number == 0) + { + /* get pagenumber */ + b2 = dehamming[vtxt_row[3]]; + b3 = dehamming[vtxt_row[2]]; + + if (b2 == 0xFF || b3 == 0xFF) + { + m_TXTCache.CurrentPage[magazine] = m_TXTCache.PageReceiving = -1; + continue; + } + + m_TXTCache.CurrentPage[magazine] = m_TXTCache.PageReceiving = magazine<<8 | b2<<4 | b3; + + if (b2 == 0x0f && b3 == 0x0f) + { + m_TXTCache.CurrentSubPage[magazine] = -1; /* ?ff: ignore data transmissions */ + continue; + } + + /* get subpagenumber */ + b1 = dehamming[vtxt_row[7]]; + b2 = dehamming[vtxt_row[6]]; + b3 = dehamming[vtxt_row[5]]; + b4 = dehamming[vtxt_row[4]]; + + if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF || b4 == 0xFF) + { + m_TXTCache.CurrentSubPage[magazine] = -1; + continue; + } + + b1 &= 3; + b3 &= 7; + + if (IsDec(m_TXTCache.PageReceiving)) /* ignore other subpage bits for hex pages */ + m_TXTCache.CurrentSubPage[magazine] = b3<<4 | b4; + else + m_TXTCache.CurrentSubPage[magazine] = b4; /* max 16 subpages for hex pages */ + + /* store current subpage for this page */ + m_TXTCache.SubPageTable[m_TXTCache.CurrentPage[magazine]] = m_TXTCache.CurrentSubPage[magazine]; + + AllocateCache(magazine); + LoadPage(m_TXTCache.CurrentPage[magazine], m_TXTCache.CurrentSubPage[magazine], pagedata[magazine]); + pageinfo_thread = &(m_TXTCache.astCachetable[m_TXTCache.CurrentPage[magazine]][m_TXTCache.CurrentSubPage[magazine]]->pageinfo); + if (!pageinfo_thread) + continue; + + if ((m_TXTCache.PageReceiving & 0xff) == 0xfe) /* ?fe: magazine organization table (MOT) */ + pageinfo_thread->function = FUNC_MOT; + + /* check controlbits */ + if (dehamming[vtxt_row[5]] & 8) /* C4 -> erase page */ + { + memset(m_TXTCache.astCachetable[m_TXTCache.CurrentPage[magazine]][m_TXTCache.CurrentSubPage[magazine]]->data, ' ', 23*40); + memset(pagedata[magazine],' ', 23*40); + } + if (dehamming[vtxt_row[9]] & 8) /* C8 -> update page */ + doupdate = m_TXTCache.PageReceiving; + + pageinfo_thread->boxed = !!(dehamming[vtxt_row[7]] & 0x0c); + + /* get country control bits */ + b1 = dehamming[vtxt_row[9]]; + if (b1 != 0xFF) + { + pageinfo_thread->nationalvalid = 1; + pageinfo_thread->national = rev_lut[b1] & 0x07; + } + + if (dehamming[vtxt_row[7]] & 0x08)// subtitle page + { + int i = 0, found = -1, use = -1; + for (; i < 8; i++) + { + if (use == -1 && !m_TXTCache.SubtitlePages[i].page) + use = i; + else if (m_TXTCache.SubtitlePages[i].page == m_TXTCache.PageReceiving) + { + found = i; + use = i; + break; + } + } + if (found == -1 && use != -1) + m_TXTCache.SubtitlePages[use].page = m_TXTCache.PageReceiving; + if (use != -1) + m_TXTCache.SubtitlePages[use].language = CountryConversionTable[pageinfo_thread->national]; + } + + /* check parity, copy line 0 to cache (start and end 8 bytes are not needed and used otherwise) */ + unsigned char *p = m_TXTCache.astCachetable[m_TXTCache.CurrentPage[magazine]][m_TXTCache.CurrentSubPage[magazine]]->p0; + for (int i = 10; i < 42-8; i++) + *p++ = deparity[vtxt_row[i]]; + + if (!IsDec(m_TXTCache.PageReceiving)) + continue; /* valid hex page number: just copy headline, ignore TimeString */ + + /* copy TimeString */ + p = m_TXTCache.TimeString; + for (int i = 42-8; i < 42; i++) + *p++ = deparity[vtxt_row[i]]; + } + else if (packet_number == 29 && dehamming[vtxt_row[2]]== 0) /* packet 29/0 replaces 28/0 for a whole magazine */ + { + Decode_p2829(vtxt_row, &(m_TXTCache.astP29[magazine])); + } + else if (m_TXTCache.CurrentPage[magazine] != -1 && m_TXTCache.CurrentSubPage[magazine] != -1) + /* packet>0, 0 has been correctly received, buffer allocated */ + { + pageinfo_thread = &(m_TXTCache.astCachetable[m_TXTCache.CurrentPage[magazine]][m_TXTCache.CurrentSubPage[magazine]]->pageinfo); + if (!pageinfo_thread) + continue; + + /* pointer to current info struct */ + if (packet_number <= 25) + { + unsigned char *p = NULL; + if (packet_number < 24) + { + p = pagedata[magazine] + 40*(packet_number-1); + } + else + { + if (!(pageinfo_thread->p24)) + pageinfo_thread->p24 = (unsigned char*) calloc(2, 40); + if (pageinfo_thread->p24) + p = pageinfo_thread->p24 + (packet_number - 24) * 40; + } + if (p) + { + if (IsDec(m_TXTCache.CurrentPage[magazine])) + { + for (int i = 2; i < 42; i++) + { + *p++ = vtxt_row[i] & 0x7f; /* allow values with parity errors as some channels don't care :( */ + } + } + else if ((m_TXTCache.CurrentPage[magazine] & 0xff) == 0xfe) + { + for (int i = 2; i < 42; i++) + { + *p++ = dehamming[vtxt_row[i]]; /* decode hamming 8/4 */ + } + } + else /* other hex page: no parity check, just copy */ + memcpy(p, &vtxt_row[2], 40); + } + } + else if (packet_number == 27) + { + int descode = dehamming[vtxt_row[2]]; /* designation code (0..15) */ + if (descode == 0xff) + continue; + + if (descode == 0) // reading FLOF-Pagelinks + { + b1 = dehamming[vtxt_row[0]]; + if (b1 != 0xff) + { + b1 &= 7; + + for (int i = 0; i < FLOFSIZE; i++) + { + b2 = dehamming[vtxt_row[4+i*6]]; + b3 = dehamming[vtxt_row[3+i*6]]; + + if (b2 != 0xff && b3 != 0xff) + { + b4 = ((b1 ^ (dehamming[vtxt_row[8+i*6]]>>1)) & 6) | ((b1 ^ (dehamming[vtxt_row[6+i*6]]>>3)) & 1); + if (b4 == 0) + b4 = 8; + if (b2 <= 9 && b3 <= 9) + m_TXTCache.FlofPages[m_TXTCache.CurrentPage[magazine] ][i] = b4<<8 | b2<<4 | b3; + } + } + + /* copy last 2 links to ADIPTable for TOP-Index */ + if (pageinfo_thread->p24) /* packet 24 received */ + { + int a, a1, e=39, l=3; + unsigned char *p = pageinfo_thread->p24; + do + { + for (; + l >= 2 && 0 == m_TXTCache.FlofPages[m_TXTCache.CurrentPage[magazine]][l]; + l--) + ; /* find used linkindex */ + for (; + e >= 1 && !isalnum(p[e]); + e--) + ; /* find end */ + for (a = a1 = e - 1; + a >= 0 && p[a] >= ' '; + a--) /* find start */ + if (p[a] > ' ') + a1 = a; /* first non-space */ + if (a >= 0 && l >= 2) + { + strncpy(m_TXTCache.ADIPTable[m_TXTCache.FlofPages[m_TXTCache.CurrentPage[magazine]][l]], (const char*) &p[a1], 12); + if (e-a1 < 11) + m_TXTCache.ADIPTable[m_TXTCache.FlofPages[m_TXTCache.CurrentPage[magazine]][l]][e-a1+1] = '\0'; + } + e = a - 1; + l--; + } while (l >= 2); + } + } + } + else if (descode == 4) /* level 2.5 links (ignore level 3.5 links of /4 and /5) */ + { + int i; + Textp27_t *p; + + if (!pageinfo_thread->ext) + pageinfo_thread->ext = (TextExtData_t*) calloc(1, sizeof(TextExtData_t)); + if (!pageinfo_thread->ext) + continue; + if (!(pageinfo_thread->ext->p27)) + pageinfo_thread->ext->p27 = (Textp27_t*) calloc(4, sizeof(Textp27_t)); + if (!(pageinfo_thread->ext->p27)) + continue; + p = pageinfo_thread->ext->p27; + for (i = 0; i < 4; i++) + { + int d1 = CDVDTeletextTools::deh24(&vtxt_row[6*i + 3]); + int d2 = CDVDTeletextTools::deh24(&vtxt_row[6*i + 6]); + if (d1 < 0 || d2 < 0) + continue; + + p->local = i & 0x01; + p->drcs = !!(i & 0x02); + p->l25 = !!(d1 & 0x04); + p->l35 = !!(d1 & 0x08); + p->page = + (((d1 & 0x000003c0) >> 6) | + ((d1 & 0x0003c000) >> (14-4)) | + ((d1 & 0x00003800) >> (11-8))) ^ + (dehamming[vtxt_row[0]] << 8); + if (p->page < 0x100) + p->page += 0x800; + p->subpage = d2 >> 2; + if ((p->page & 0xff) == 0xff) + p->page = 0; + else if (p->page > 0x899) + { + // workaround for crash on RTL Shop ... + // sorry.. i dont understand whats going wrong here :) + continue; + } + else if (m_TXTCache.astCachetable[p->page][0]) /* link valid && linked page cached */ + { + TextPageinfo_t *pageinfo_link = &(m_TXTCache.astCachetable[p->page][0]->pageinfo); + if (p->local) + pageinfo_link->function = p->drcs ? FUNC_DRCS : FUNC_POP; + else + pageinfo_link->function = p->drcs ? FUNC_GDRCS : FUNC_GPOP; + } + p++; /* */ + } + } + } + else if (packet_number == 26) + { + int descode = dehamming[vtxt_row[2]]; /* designation code (0..15) */ + if (descode == 0xff) + continue; + + if (!pageinfo_thread->ext) + pageinfo_thread->ext = (TextExtData_t*) calloc(1, sizeof(TextExtData_t)); + if (!pageinfo_thread->ext) + continue; + if (!(pageinfo_thread->ext->p26[descode])) + pageinfo_thread->ext->p26[descode] = (unsigned char*) malloc(13 * 3); + if (pageinfo_thread->ext->p26[descode]) + memcpy(pageinfo_thread->ext->p26[descode], &vtxt_row[3], 13 * 3); + } + else if (packet_number == 28) + { + int descode = dehamming[vtxt_row[2]]; /* designation code (0..15) */ + + if (descode == 0xff) + continue; + + if (descode != 2) + { + int t1 = CDVDTeletextTools::deh24(&vtxt_row[7-4]); + pageinfo_thread->function = t1 & 0x0f; + if (!pageinfo_thread->nationalvalid) + { + pageinfo_thread->nationalvalid = 1; + pageinfo_thread->national = (t1>>4) & 0x07; + } + } + + switch (descode) /* designation code */ + { + case 0: /* basic level 1 page */ + Decode_p2829(vtxt_row, &(pageinfo_thread->ext)); + break; + case 1: /* G0/G1 designation for older decoders, level 3.5: DCLUT4/16, colors for multicolored bitmaps */ + break; /* ignore */ + case 2: /* page key */ + break; /* ignore */ + case 3: /* types of PTUs in DRCS */ + break; /* TODO */ + case 4: /* CLUTs 0/1, only level 3.5 */ + break; /* ignore */ + default: + break; /* invalid, ignore */ + } /* switch designation code */ + } + else if (packet_number == 30) + { + m_TXTCache.line30 = ""; + for (int i=26-4; i <= 45-4; i++) /* station ID */ + m_TXTCache.line30.append(1, deparity[vtxt_row[i]]); + } + } + + /* set update flag */ + if (m_TXTCache.CurrentPage[magazine] == m_TXTCache.Page && m_TXTCache.CurrentSubPage[magazine] != -1) + { + SavePage(m_TXTCache.CurrentPage[magazine], m_TXTCache.CurrentSubPage[magazine], pagedata[magazine]); + m_TXTCache.PageUpdate = true; + doupdate = 0; + if (!m_TXTCache.ZapSubpageManual) + m_TXTCache.SubPage = m_TXTCache.CurrentSubPage[magazine]; + } + } + } + } + } + else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) + { + m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value; + } + else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (CDVDTeletextData::Flush()) + { + ResetTeletextCache(); + } + pMsg->Release(); + } +} + +void CDVDTeletextData::OnExit() +{ + CLog::Log(LOGNOTICE, "thread end: data_thread"); +} + +void CDVDTeletextData::Flush() +{ + /* flush using message as this get's called from dvdplayer thread */ + /* and any demux packet that has been taken out of queue need to */ + /* be disposed of before we flush */ + m_messageQueue.Flush(); + m_messageQueue.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH)); +} + +void CDVDTeletextData::Decode_p2829(unsigned char *vtxt_row, TextExtData_t **ptExtData) +{ + int bitsleft, colorindex; + unsigned char *p; + int t1 = CDVDTeletextTools::deh24(&vtxt_row[7-4]); + int t2 = CDVDTeletextTools::deh24(&vtxt_row[10-4]); + + if (t1 < 0 || t2 < 0) + return; + + if (!(*ptExtData)) + (*ptExtData) = (TextExtData_t*) calloc(1, sizeof(TextExtData_t)); + if (!(*ptExtData)) + return; + + (*ptExtData)->p28Received = 1; + (*ptExtData)->DefaultCharset = (t1>>7) & 0x7f; + (*ptExtData)->SecondCharset = ((t1>>14) & 0x0f) | ((t2<<4) & 0x70); + (*ptExtData)->LSP = !!(t2 & 0x08); + (*ptExtData)->RSP = !!(t2 & 0x10); + (*ptExtData)->SPL25 = !!(t2 & 0x20); + (*ptExtData)->LSPColumns = (t2>>6) & 0x0f; + + bitsleft = 8; /* # of bits not evaluated in val */ + t2 >>= 10; /* current data */ + p = &vtxt_row[13-4]; /* pointer to next data triplet */ + for (colorindex = 0; colorindex < 16; colorindex++) + { + if (bitsleft < 12) + { + t2 |= CDVDTeletextTools::deh24(p) << bitsleft; + if (t2 < 0) /* hamming error */ + break; + p += 3; + bitsleft += 18; + } + (*ptExtData)->bgr[colorindex] = t2 & 0x0fff; + bitsleft -= 12; + t2 >>= 12; + } + if (t2 < 0 || bitsleft != 14) + { + (*ptExtData)->p28Received = 0; + return; + } + (*ptExtData)->DefScreenColor = t2 & 0x1f; + t2 >>= 5; + (*ptExtData)->DefRowColor = t2 & 0x1f; + (*ptExtData)->BlackBgSubst = !!(t2 & 0x20); + t2 >>= 6; + (*ptExtData)->ColorTableRemapping = t2 & 0x07; +} + +void CDVDTeletextData::SavePage(int p, int sp, unsigned char* buffer) +{ + CSingleLock lock(m_critSection); + TextCachedPage_t* pg = m_TXTCache.astCachetable[p][sp]; + if (!pg) + { + CLog::Log(LOGERROR, "CDVDTeletextData: trying to save a not allocated page!!"); + return; + } + + memcpy(pg->data, buffer, 23*40); +} + +void CDVDTeletextData::LoadPage(int p, int sp, unsigned char* buffer) +{ + CSingleLock lock(m_critSection); + TextCachedPage_t* pg = m_TXTCache.astCachetable[p][sp]; + if (!pg) + { + CLog::Log(LOGERROR, "CDVDTeletextData: trying to load a not allocated page!!"); + return; + } + + memcpy(buffer, pg->data, 23*40); +} + +void CDVDTeletextData::ErasePage(int magazine) +{ + CSingleLock lock(m_critSection); + TextCachedPage_t* pg = m_TXTCache.astCachetable[m_TXTCache.CurrentPage[magazine]][m_TXTCache.CurrentSubPage[magazine]]; + if (pg) + { + memset(&(pg->pageinfo), 0, sizeof(TextPageinfo_t)); /* struct pageinfo */ + memset(pg->p0, ' ', 24); + memset(pg->data, ' ', 23*40); + } +} + +void CDVDTeletextData::AllocateCache(int magazine) +{ + /* check cachetable and allocate memory if needed */ + if (m_TXTCache.astCachetable[m_TXTCache.CurrentPage[magazine]][m_TXTCache.CurrentSubPage[magazine]] == 0) + { + m_TXTCache.astCachetable[m_TXTCache.CurrentPage[magazine]][m_TXTCache.CurrentSubPage[magazine]] = new TextCachedPage_t; + if (m_TXTCache.astCachetable[m_TXTCache.CurrentPage[magazine]][m_TXTCache.CurrentSubPage[magazine]] ) + { + ErasePage(magazine); + m_TXTCache.CachedPages++; + } + } +} diff --git a/xbmc/cores/dvdplayer/DVDPlayerTeletext.h b/xbmc/cores/dvdplayer/DVDPlayerTeletext.h new file mode 100644 index 0000000000..8a8141660c --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDPlayerTeletext.h @@ -0,0 +1,67 @@ +#pragma once + +/* + * Copyright (C) 2005-2009 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "../../utils/Thread.h" +#include "DVDMessageQueue.h" +#include "utils/TeletextDefines.h" + +class CDVDStreamInfo; + +class CDVDTeletextData : public CThread +{ +public: + CDVDTeletextData(); + ~CDVDTeletextData(); + + bool CheckStream(CDVDStreamInfo &hints); + bool OpenStream(CDVDStreamInfo &hints); + void CloseStream(bool bWaitForBuffers); + void Flush(); + + // waits until all available data has been rendered + void WaitForBuffers() { m_messageQueue.WaitUntilEmpty(); } + bool AcceptsData() { return !m_messageQueue.IsFull(); } + void SendMessage(CDVDMsg* pMsg) { m_messageQueue.Put(pMsg); } + + TextCacheStruct_t* GetTeletextCache() { return &m_TXTCache; } + void LoadPage(int p, int sp, unsigned char* buffer); + + CDVDMessageQueue m_messageQueue; + +protected: + virtual void OnStartup(); + virtual void OnExit(); + virtual void Process(); + +private: + void ResetTeletextCache(); + void Decode_p2829(unsigned char *vtxt_row, TextExtData_t **ptExtData); + void SavePage(int p, int sp, unsigned char* buffer); + void ErasePage(int magazine); + void AllocateCache(int magazine); + + int m_speed; + TextCacheStruct_t m_TXTCache; + CCriticalSection m_critSection; +}; + diff --git a/xbmc/cores/dvdplayer/Makefile b/xbmc/cores/dvdplayer/Makefile index 09ba30dece..9985b80a17 100644 --- a/xbmc/cores/dvdplayer/Makefile +++ b/xbmc/cores/dvdplayer/Makefile @@ -15,6 +15,7 @@ SRCS= DVDAudio.cpp \ DVDPlayer.cpp \ DVDPlayerSubtitle.cpp \ DVDPlayerVideo.cpp \ + DVDPlayerTeletext.cpp \ DVDStreamInfo.cpp \ DVDFileInfo.cpp \ DVDPlayerAudioResampler.cpp \ diff --git a/xbmc/cores/ffmpeg/avcodec.h b/xbmc/cores/ffmpeg/avcodec.h index f6175aca95..090b4c18b7 100644 --- a/xbmc/cores/ffmpeg/avcodec.h +++ b/xbmc/cores/ffmpeg/avcodec.h @@ -329,6 +329,11 @@ enum CodecID { CODEC_ID_SSA, CODEC_ID_MOV_TEXT, + /* data codecs */ + CODEC_ID_VBI_DATA= 0x17500, + CODEC_ID_VBI_TELETEXT, + CODEC_ID_EBU_TELETEXT, + /* other specific kind of codecs (generally used for attachments) */ CODEC_ID_TTF= 0x18000, diff --git a/xbmc/cores/paplayer/MPCCodec/include/config.h.in b/xbmc/cores/paplayer/MPCCodec/include/config.h.in index 286a213ee4..99b28ea11d 100644 --- a/xbmc/cores/paplayer/MPCCodec/include/config.h.in +++ b/xbmc/cores/paplayer/MPCCodec/include/config.h.in @@ -1,5 +1,8 @@ /* include/config.h.in. Generated from configure.ac by autoheader. */ +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + /* Define to 1 if you have the <dlfcn.h> header file. */ #undef HAVE_DLFCN_H @@ -84,9 +87,17 @@ /* Version number of package */ #undef VERSION -/* Define to 1 if your processor stores words with the most significant byte - first (like Motorola and SPARC, unlike Intel and VAX). */ -#undef WORDS_BIGENDIAN +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif /* Define to empty if `const' does not conform to ANSI C. */ #undef const diff --git a/xbmc/utils/GUIInfoManager.cpp b/xbmc/utils/GUIInfoManager.cpp index 538e22d02c..0875448f1e 100644 --- a/xbmc/utils/GUIInfoManager.cpp +++ b/xbmc/utils/GUIInfoManager.cpp @@ -64,6 +64,7 @@ #include "LocalizeStrings.h" #include "CPUInfo.h" #include "StringUtils.h" +#include "TeletextDefines.h" // stuff for current song #include "MusicInfoTagLoaderFactory.h" @@ -543,6 +544,7 @@ int CGUIInfoManager::TranslateSingleString(const CStdString &strCondition) else if (strTest.Equals("videoplayer.videoaspect")) return VIDEOPLAYER_VIDEO_ASPECT; else if (strTest.Equals("videoplayer.audiocodec")) return VIDEOPLAYER_AUDIO_CODEC; else if (strTest.Equals("videoplayer.audiochannels")) return VIDEOPLAYER_AUDIO_CHANNELS; + else if (strTest.Equals("videoplayer.hasteletext")) return VIDEOPLAYER_HASTELETEXT; } else if (strCategory.Equals("playlist")) { @@ -1937,6 +1939,10 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI case PLAYER_HASDURATION: bReturn = g_application.GetTotalTime() > 0; break; + case VIDEOPLAYER_HASTELETEXT: + if (g_application.m_pPlayer->GetTeletextCache()) + bReturn = true; + break; case VISUALISATION_LOCKED: { CGUIMessage msg(GUI_MSG_GET_VISUALISATION, 0, 0); @@ -2926,7 +2932,9 @@ CStdString CGUIInfoManager::GetVideoLabel(int item) { if (m_currentFile->HasVideoInfoTag() && !m_currentFile->GetVideoInfoTag()->m_strTitle.IsEmpty()) return m_currentFile->GetVideoInfoTag()->m_strTitle; - // don't have the title, so use label, or drop down to title from path + // don't have the title, so use dvdplayer, label, or drop down to title from path + if (!g_application.m_pPlayer->GetPlayingTitle().IsEmpty()) + return g_application.m_pPlayer->GetPlayingTitle(); if (!m_currentFile->GetLabel().IsEmpty()) return m_currentFile->GetLabel(); return CUtil::GetTitleFromPath(m_currentFile->m_strPath); diff --git a/xbmc/utils/GUIInfoManager.h b/xbmc/utils/GUIInfoManager.h index 39324ab421..df2f9ee397 100644 --- a/xbmc/utils/GUIInfoManager.h +++ b/xbmc/utils/GUIInfoManager.h @@ -245,6 +245,7 @@ class CDateTime; #define VIDEOPLAYER_AUDIO_CODEC 288 #define VIDEOPLAYER_AUDIO_CHANNELS 289 #define VIDEOPLAYER_VIDEO_ASPECT 290 +#define VIDEOPLAYER_HASTELETEXT 291 #define AUDIOSCROBBLER_ENABLED 300 #define AUDIOSCROBBLER_CONN_STATE 301 diff --git a/xbmc/utils/Makefile b/xbmc/utils/Makefile index 287d891816..da937c31d2 100644 --- a/xbmc/utils/Makefile +++ b/xbmc/utils/Makefile @@ -1,4 +1,4 @@ -INCLUDES=-I. -I.. -I../../ -I../linux -I../../guilib +INCLUDES=-I. -I.. -I../../ -I../linux -I../../guilib -I/usr/include/freetype2 SRCS=AlarmClock.cpp \ Archive.cpp \ @@ -55,7 +55,8 @@ SRCS=AlarmClock.cpp \ LockFree.cpp \ StreamDetails.cpp \ TimeUtils.cpp \ - JobManager.cpp + JobManager.cpp \ + Teletext.cpp LIB=utils.a diff --git a/xbmc/utils/Teletext.cpp b/xbmc/utils/Teletext.cpp new file mode 100644 index 0000000000..8f2d161098 --- /dev/null +++ b/xbmc/utils/Teletext.cpp @@ -0,0 +1,4010 @@ +/* + * Copyright (C) 2005-2009 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/* + * Most Codeparts are taken from the TuxBox Teletext plugin which is based + * upon videotext-0.6.19991029 and written by Thomas Loewe (LazyT), + * Roland Meier and DBLuelle. See http://www.tuxtxt.net/ for more information. + * Many thanks to the TuxBox Teletext Team for this great work. + */ + +#include "Teletext.h" +#include "GUISettings.h" +#include "Application.h" +#include "utils/log.h" +#include "FileSystem/SpecialProtocol.h" +#include "Settings.h" + +using namespace std; + +static const char *TeletextFont = "special://xbmc/media/Fonts/teletext.ttf"; + +/* spacing attributes */ +#define alpha_black 0x00 +#define alpha_red 0x01 +#define alpha_green 0x02 +#define alpha_yellow 0x03 +#define alpha_blue 0x04 +#define alpha_magenta 0x05 +#define alpha_cyan 0x06 +#define alpha_white 0x07 +#define flash 0x08 +#define steady 0x09 +#define end_box 0x0A +#define start_box 0x0B +#define normal_size 0x0C +#define double_height 0x0D +#define double_width 0x0E +#define double_size 0x0F +#define mosaic_black 0x10 +#define mosaic_red 0x11 +#define mosaic_green 0x12 +#define mosaic_yellow 0x13 +#define mosaic_blue 0x14 +#define mosaic_magenta 0x15 +#define mosaic_cyan 0x16 +#define mosaic_white 0x17 +#define conceal 0x18 +#define contiguous_mosaic 0x19 +#define separated_mosaic 0x1A +#define esc 0x1B +#define black_background 0x1C +#define new_background 0x1D +#define hold_mosaic 0x1E +#define release_mosaic 0x1F + +#define RowAddress2Row(row) ((row == 40) ? 24 : (row - 40)) + +// G2 Set as defined in ETS 300 706 +const unsigned short int G2table[4][6*16] = +{ + // Latin G2 Supplementary Set + { 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x0024, 0x00A5, 0x0023, 0x00A7, 0x00A4, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x0020, 0x0300, 0x0301, 0x02C6, 0x0303, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7, + 0x2014, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x0020, 0x0020, 0x0020, 0x215B, 0x215C, 0x215D, 0x215E, + 0x2126, 0x00C6, 0x00D0, 0x00AA, 0x0126, 0x0020, 0x0132, 0x013F, 0x0141, 0x00D8, 0x0152, 0x00BA, 0x00DE, 0x0166, 0x014A, 0x0149, + 0x0138, 0x00E6, 0x0111, 0x00F0, 0x0127, 0x0131, 0x0133, 0x0140, 0x0142, 0x00F8, 0x0153, 0x00DF, 0x00FE, 0x0167, 0x014B, 0x25A0}, + // Cyrillic G2 Supplementary Set + { 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x0024, 0x00A5, 0x0020, 0x00A7, 0x0020, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x0020, 0x0300, 0x0301, 0x02C6, 0x02DC, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7, + 0x2014, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x0141, 0x0142, 0x00DF, 0x215B, 0x215C, 0x215D, 0x215E, + 0x0044, 0x0045, 0x0046, 0x0047, 0x0049, 0x004A, 0x004B, 0x004C, 0x004E, 0x0051, 0x0052, 0x0053, 0x0055, 0x0056, 0x0057, 0x005A, + 0x0064, 0x0065, 0x0066, 0x0067, 0x0069, 0x006A, 0x006B, 0x006C, 0x006E, 0x0071, 0x0072, 0x0073, 0x0075, 0x0076, 0x0077, 0x007A}, + // Greek G2 Supplementary Set + { 0x0020, 0x0061, 0x0062, 0x00A3, 0x0065, 0x0068, 0x0069, 0x00A7, 0x003A, 0x2018, 0x201C, 0x006B, 0x2190, 0x2191, 0x2192, 0x2193, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x006D, 0x006E, 0x0070, 0x00F7, 0x2019, 0x201D, 0x0074, 0x00BC, 0x00BD, 0x00BE, 0x0078, + 0x0020, 0x0300, 0x0301, 0x02C6, 0x02DC, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7, + 0x003F, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x038A, 0x038E, 0x038F, 0x215B, 0x215C, 0x215D, 0x215E, + 0x0043, 0x0044, 0x0046, 0x0047, 0x004A, 0x004C, 0x0051, 0x0052, 0x0053, 0x0055, 0x0056, 0x0057, 0x0059, 0x005A, 0x0386, 0x0389, + 0x0063, 0x0064, 0x0066, 0x0067, 0x006A, 0x006C, 0x0071, 0x0072, 0x0073, 0x0075, 0x0076, 0x0077, 0x0079, 0x007A, 0x0388, 0x25A0}, + // Arabic G2 Set + { 0x0020, 0x0639, 0xFEC9, 0xFE83, 0xFE85, 0xFE87, 0xFE8B, 0xFE89, 0xFB7C, 0xFB7D, 0xFB7A, 0xFB58, 0xFB59, 0xFB56, 0xFB6D, 0xFB8E, + 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFECE, 0xFECD, 0xFEFC, 0xFEEC, 0xFEEA, 0xFEE9, + 0x00E0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00EB, 0x00EA, 0x00F9, 0x00EE, 0xFECA, + 0x00E9, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E2, 0x00F4, 0x00FB, 0x00E7, 0x25A0} +}; + +//const (avoid warnings :<) +TextPageAttr_t Text_AtrTable[] = +{ + { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_WB */ + { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 1 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_PassiveDefault */ + { TXT_ColorWhite , TXT_ColorRed , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L250 */ + { TXT_ColorBlack , TXT_ColorYellow, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L252 */ + { TXT_ColorBlack , TXT_ColorGreen , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L251 */ + { TXT_ColorWhite , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L253 */ + { TXT_ColorMagenta, TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU0 */ + { TXT_ColorGreen , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU1 */ + { TXT_ColorYellow , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU2 */ + { TXT_ColorCyan , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU3 */ + { TXT_ColorMenu2 , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG0 */ + { TXT_ColorYellow , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG1 */ + { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG2 */ + { TXT_ColorWhite , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG3 */ + { TXT_ColorMenu2 , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM0 */ + { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM1 */ + { TXT_ColorMenu2 , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM2 */ + { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM3 */ + { TXT_ColorMenu1 , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL0 5a Z */ + { TXT_ColorWhite , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL1 58 X */ + { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL2 9b › */ + { TXT_ColorMenu2 , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU0 ab « */ + { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU1 a4 ¤ */ + { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU2 9b › */ + { TXT_ColorMenu2 , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU3 cb Ë */ + { TXT_ColorCyan , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU4 c7 Ç */ + { TXT_ColorWhite , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU5 c8 È */ + { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU6 a8 ¨ */ + { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_CATCHMENU0 a4 ¤ */ + { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f} /* ATR_CATCHMENU1 a8 ¨ */ +}; + +/* shapes */ +enum +{ + S_END = 0, + S_FHL, /* full horizontal line: y-offset */ + S_FVL, /* full vertical line: x-offset */ + S_BOX, /* rectangle: x-offset, y-offset, width, height */ + S_TRA, /* trapez: x0, y0, l0, x1, y1, l1 */ + S_BTR, /* trapez in bgcolor: x0, y0, l0, x1, y1, l1 */ + S_INV, /* invert */ + S_LNK, /* call other shape: shapenumber */ + S_CHR, /* Character from freetype hibyte, lowbyte */ + S_ADT, /* Character 2F alternating raster */ + S_FLH, /* flip horizontal */ + S_FLV /* flip vertical */ +}; + +/* shape coordinates */ +enum +{ + S_W13 = 5, /* width*1/3 */ + S_W12, /* width*1/2 */ + S_W23, /* width*2/3 */ + S_W11, /* width */ + S_WM3, /* width-3 */ + S_H13, /* height*1/3 */ + S_H12, /* height*1/2 */ + S_H23, /* height*2/3 */ + S_H11, /* height */ + S_NrShCoord +}; + +/* G3 characters */ +unsigned char aG3_20[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W12, S_END }; +unsigned char aG3_21[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W11, S_END }; +unsigned char aG3_22[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W12, S_END }; +unsigned char aG3_23[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W11, S_END }; +unsigned char aG3_24[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W12, S_END }; +unsigned char aG3_25[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W11, S_END }; +unsigned char aG3_26[] = { S_INV, S_LNK, 0x66, S_END }; +unsigned char aG3_27[] = { S_INV, S_LNK, 0x67, S_END }; +unsigned char aG3_28[] = { S_INV, S_LNK, 0x68, S_END }; +unsigned char aG3_29[] = { S_INV, S_LNK, 0x69, S_END }; +unsigned char aG3_2a[] = { S_INV, S_LNK, 0x6a, S_END }; +unsigned char aG3_2b[] = { S_INV, S_LNK, 0x6b, S_END }; +unsigned char aG3_2c[] = { S_INV, S_LNK, 0x6c, S_END }; +unsigned char aG3_2d[] = { S_INV, S_LNK, 0x6d, S_END }; +unsigned char aG3_2e[] = { S_BOX, 2, 0, 3, S_H11, S_END }; +unsigned char aG3_2f[] = { S_ADT }; +unsigned char aG3_30[] = { S_LNK, 0x20, S_FLH, S_END }; +unsigned char aG3_31[] = { S_LNK, 0x21, S_FLH, S_END }; +unsigned char aG3_32[] = { S_LNK, 0x22, S_FLH, S_END }; +unsigned char aG3_33[] = { S_LNK, 0x23, S_FLH, S_END }; +unsigned char aG3_34[] = { S_LNK, 0x24, S_FLH, S_END }; +unsigned char aG3_35[] = { S_LNK, 0x25, S_FLH, S_END }; +unsigned char aG3_36[] = { S_INV, S_LNK, 0x76, S_END }; +unsigned char aG3_37[] = { S_INV, S_LNK, 0x77, S_END }; +unsigned char aG3_38[] = { S_INV, S_LNK, 0x78, S_END }; +unsigned char aG3_39[] = { S_INV, S_LNK, 0x79, S_END }; +unsigned char aG3_3a[] = { S_INV, S_LNK, 0x7a, S_END }; +unsigned char aG3_3b[] = { S_INV, S_LNK, 0x7b, S_END }; +unsigned char aG3_3c[] = { S_INV, S_LNK, 0x7c, S_END }; +unsigned char aG3_3d[] = { S_INV, S_LNK, 0x7d, S_END }; +unsigned char aG3_3e[] = { S_LNK, 0x2e, S_FLH, S_END }; +unsigned char aG3_3f[] = { S_BOX, 0, 0, S_W11, S_H11, S_END }; +unsigned char aG3_40[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_END }; +unsigned char aG3_41[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_FLV, S_END }; +unsigned char aG3_42[] = { S_LNK, 0x50, S_BOX, S_W12, S_H13, S_W12, S_H13, S_END }; +unsigned char aG3_43[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W12, S_H13, S_END }; +unsigned char aG3_44[] = { S_LNK, 0x48, S_FLV, S_LNK, 0x48, S_END }; +unsigned char aG3_45[] = { S_LNK, 0x44, S_FLH, S_END }; +unsigned char aG3_46[] = { S_LNK, 0x47, S_FLV, S_END }; +unsigned char aG3_47[] = { S_LNK, 0x48, S_FLH, S_LNK, 0x48, S_END }; +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 }; +unsigned char aG3_49[] = { S_LNK, 0x48, S_FLH, S_END }; +unsigned char aG3_4a[] = { S_LNK, 0x48, S_FLV, S_END }; +unsigned char aG3_4b[] = { S_LNK, 0x48, S_FLH, S_FLV, S_END }; +unsigned char aG3_4c[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W11, S_H13, S_END }; +unsigned char aG3_4d[] = { S_CHR, 0x25, 0xE6 }; +unsigned char aG3_4e[] = { S_CHR, 0x25, 0xCF }; +unsigned char aG3_4f[] = { S_CHR, 0x25, 0xCB }; +unsigned char aG3_50[] = { S_BOX, S_W12, 0, 2, S_H11, S_FLH, S_BOX, S_W12, 0, 2, S_H11,S_END }; +unsigned char aG3_51[] = { S_BOX, 0, S_H12, S_W11, 2, S_FLV, S_BOX, 0, S_H12, S_W11, 2,S_END }; +unsigned char aG3_52[] = { S_LNK, 0x55, S_FLH, S_FLV, S_END }; +unsigned char aG3_53[] = { S_LNK, 0x55, S_FLV, S_END }; +unsigned char aG3_54[] = { S_LNK, 0x55, S_FLH, S_END }; +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 }; +unsigned char aG3_56[] = { S_LNK, 0x57, S_FLH, S_END}; +unsigned char aG3_57[] = { S_LNK, 0x55, S_LNK, 0x50 , S_END}; +unsigned char aG3_58[] = { S_LNK, 0x59, S_FLV, S_END}; +unsigned char aG3_59[] = { S_LNK, 0x7e, S_LNK, 0x51 , S_END}; +unsigned char aG3_5a[] = { S_LNK, 0x50, S_LNK, 0x51 , S_END}; +unsigned char aG3_5b[] = { S_CHR, 0x21, 0x92}; +unsigned char aG3_5c[] = { S_CHR, 0x21, 0x90}; +unsigned char aG3_5d[] = { S_CHR, 0x21, 0x91}; +unsigned char aG3_5e[] = { S_CHR, 0x21, 0x93}; +unsigned char aG3_5f[] = { S_CHR, 0x00, 0x20}; +unsigned char aG3_60[] = { S_INV, S_LNK, 0x20, S_END }; +unsigned char aG3_61[] = { S_INV, S_LNK, 0x21, S_END }; +unsigned char aG3_62[] = { S_INV, S_LNK, 0x22, S_END }; +unsigned char aG3_63[] = { S_INV, S_LNK, 0x23, S_END }; +unsigned char aG3_64[] = { S_INV, S_LNK, 0x24, S_END }; +unsigned char aG3_65[] = { S_INV, S_LNK, 0x25, S_END }; +unsigned char aG3_66[] = { S_LNK, 0x20, S_FLV, S_END }; +unsigned char aG3_67[] = { S_LNK, 0x21, S_FLV, S_END }; +unsigned char aG3_68[] = { S_LNK, 0x22, S_FLV, S_END }; +unsigned char aG3_69[] = { S_LNK, 0x23, S_FLV, S_END }; +unsigned char aG3_6a[] = { S_LNK, 0x24, S_FLV, S_END }; +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 }; +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 }; +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 }; +unsigned char aG3_6e[] = { S_CHR, 0x00, 0x20}; +unsigned char aG3_6f[] = { S_CHR, 0x00, 0x20}; +unsigned char aG3_70[] = { S_INV, S_LNK, 0x30, S_END }; +unsigned char aG3_71[] = { S_INV, S_LNK, 0x31, S_END }; +unsigned char aG3_72[] = { S_INV, S_LNK, 0x32, S_END }; +unsigned char aG3_73[] = { S_INV, S_LNK, 0x33, S_END }; +unsigned char aG3_74[] = { S_INV, S_LNK, 0x34, S_END }; +unsigned char aG3_75[] = { S_INV, S_LNK, 0x35, S_END }; +unsigned char aG3_76[] = { S_LNK, 0x66, S_FLH, S_END }; +unsigned char aG3_77[] = { S_LNK, 0x67, S_FLH, S_END }; +unsigned char aG3_78[] = { S_LNK, 0x68, S_FLH, S_END }; +unsigned char aG3_79[] = { S_LNK, 0x69, S_FLH, S_END }; +unsigned char aG3_7a[] = { S_LNK, 0x6a, S_FLH, S_END }; +unsigned char aG3_7b[] = { S_LNK, 0x6b, S_FLH, S_END }; +unsigned char aG3_7c[] = { S_LNK, 0x6c, S_FLH, S_END }; +unsigned char aG3_7d[] = { S_LNK, 0x6d, S_FLV, S_END }; +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) + +unsigned char *aShapes[] = +{ + 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, + 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, + 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, + 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, + 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, + 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 +}; + +// G0 Table as defined in ETS 300 706 +// cyrillic G0 Charset (0 = Serbian/Croatian, 1 = Russian/Bulgarian, 2 = Ukrainian) +const unsigned short int G0table[6][6*16] = +{ + // Cyrillic G0 Set - Option 1 - Serbian/Croatian + { ' ', '!', '\"', '#', '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', + 0x0427, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0408, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, + 0x041F, 0x040C, 0x0420, 0x0421, 0x0422, 0x0423, 0x0412, 0x0403, 0x0409, 0x040A, 0x0417, 0x040B, 0x0416, 0x0402, 0x0428, 0x040F, + 0x0447, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0458, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, + 0x043F, 0x045C, 0x0440, 0x0441, 0x0442, 0x0443, 0x0432, 0x0453, 0x0459, 0x045A, 0x0437, 0x045B, 0x0436, 0x0452, 0x0448, 0x25A0}, + // Cyrillic G0 Set - Option 2 - Russian/Bulgarian + { ' ', '!', '\"', '#', '$', '%', 0x044B, '\'', '(' , ')' , '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', + 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, + 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042A, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042B, + 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, + 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044A, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x25A0}, + // Cyrillic G0 Set - Option 3 - Ukrainian + { ' ', '!', '\"', '#', '$', '%', 0x0457, '\'', '(' , ')' , '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', + 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, + 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x0406, 0x0417, 0x0428, 0x0404, 0x0429, 0x0427, 0x0407, + 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, + 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x0456, 0x0437, 0x0448, 0x0454, 0x0449, 0x0447, 0x25A0}, + // Greek G0 Set + { ' ', '!', '\"', '#', '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', 0x00AB, '=', 0x00BB, '?', + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0x0384, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x25A0}, + // Hebrew G0 Set + { ' ', '!', 0x05F2, 0x00A3, '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x2190, 0x00BD, 0x2192, 0x2191, '#', + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x20AA, 0x2551, 0x00BE, 0x00F7, 0x25A0}, + // Arabic G0 Set - Thanks to Habib2006(fannansat) + { ' ', '!', 0x05F2, 0x00A3, '$', 0x066A, 0xFEF0, 0xFEF2, 0xFD3F, 0xFD3E, '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', 0x061B, '>', '=', '<', 0x061F, + 0xFE94, 0x0621, 0xFE92, 0x0628, 0xFE98, 0x062A, 0xFE8E, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9, + 0x0630, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0xFE9C, 0xFEA0, 0xFEA4, 0xFEA8, 0x0023, + 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFE99, 0xFE9D, 0xFEA1, 0xFEA5, 0xFEF4, + 0xFEF0, 0xFECC, 0xFED0, 0xFED4, 0xFED1, 0xFED8, 0xFED5, 0xFED9, 0xFEE0, 0xFEDD, 0xFEE4, 0xFEE1, 0xFEE8, 0xFEE5, 0xFEFB, 0x25A0} +}; + +const unsigned short int nationaltable23[14][2] = +{ + { '#', 0x00A4 }, /* 0 */ + { '#', 0x016F }, /* 1 CS/SK */ + { 0x00A3, '$' }, /* 2 EN */ + { '#', 0x00F5 }, /* 3 ET */ + { 0x00E9, 0x0457 }, /* 4 FR */ + { '#', '$' }, /* 5 DE */ + { 0x00A3, '$' }, /* 6 IT */ + { '#', '$' }, /* 7 LV/LT */ + { '#', 0x0144 }, /* 8 PL */ + { 0x00E7, '$' }, /* 9 PT/ES */ + { '#', 0x00A4 }, /* A RO */ + { '#', 0x00CB }, /* B SR/HR/SL */ + { '#', 0x00A4 }, /* C SV/FI/HU */ + { 0x20A4, 0x011F }, /* D TR */ +}; +const unsigned short int nationaltable40[14] = +{ + '@', /* 0 */ + 0x010D, /* 1 CS/SK */ + '@', /* 2 EN */ + 0x0161, /* 3 ET */ + 0x00E0, /* 4 FR */ + 0x00A7, /* 5 DE */ + 0x00E9, /* 6 IT */ + 0x0161, /* 7 LV/LT */ + 0x0105, /* 8 PL */ + 0x00A1, /* 9 PT/ES */ + 0x0162, /* A RO */ + 0x010C, /* B SR/HR/SL */ + 0x00C9, /* C SV/FI/HU */ + 0x0130, /* D TR */ +}; +const unsigned short int nationaltable5b[14][6] = +{ + { '[', '\\', ']', '^', '_', '`' }, /* 0 */ + { 0x0165, 0x017E, 0x00FD, 0x00ED, 0x0159, 0x00E9 }, /* 1 CS/SK */ + { 0x2190, 0x00BD, 0x2192, 0x2191, '#', 0x00AD }, /* 2 EN */ + { 0x00C4, 0x00D6, 0x017D, 0x00DC, 0x00D5, 0x0161 }, /* 3 ET */ + { 0x0451, 0x00EA, 0x00F9, 0x00EE, '#', 0x00E8 }, /* 4 FR */ + { 0x00C4, 0x00D6, 0x00DC, '^', '_', 0x00B0 }, /* 5 DE */ + { 0x00B0, 0x00E7, 0x2192, 0x2191, '#', 0x00F9 }, /* 6 IT */ + { 0x0117, 0x0119, 0x017D, 0x010D, 0x016B, 0x0161 }, /* 7 LV/LT */ + { 0x017B, 0x015A, 0x0141, 0x0107, 0x00F3, 0x0119 }, /* 8 PL */ + { 0x00E1, 0x00E9, 0x00ED, 0x00F3, 0x00FA, 0x00BF }, /* 9 PT/ES */ + { 0x00C2, 0x015E, 0x01CD, 0x01CF, 0x0131, 0x0163 }, /* A RO */ + { 0x0106, 0x017D, 0x00D0, 0x0160, 0x0451, 0x010D }, /* B SR/HR/SL */ + { 0x00C4, 0x00D6, 0x00C5, 0x00DC, '_', 0x00E9 }, /* C SV/FI/HU */ + { 0x015E, 0x00D6, 0x00C7, 0x00DC, 0x011E, 0x0131 }, /* D TR */ +}; +const unsigned short int nationaltable7b[14][4] = +{ + { '{', '|', '}', '~' }, /* 0 */ + { 0x00E1, 0x011B, 0x00FA, 0x0161 }, /* 1 CS/SK */ + { 0x00BC, 0x2551, 0x00BE, 0x00F7 }, /* 2 EN */ + { 0x00E4, 0x00F6, 0x017E, 0x00FC }, /* 3 ET */ + { 0x00E2, 0x00F4, 0x00FB, 0x00E7 }, /* 4 FR */ + { 0x00E4, 0x00F6, 0x00FC, 0x00DF }, /* 5 DE */ + { 0x00E0, 0x00F3, 0x00E8, 0x00EC }, /* 6 IT */ + { 0x0105, 0x0173, 0x017E, 0x012F }, /* 7 LV/LT */ + { 0x017C, 0x015B, 0x0142, 0x017A }, /* 8 PL */ + { 0x00FC, 0x00F1, 0x00E8, 0x00E0 }, /* 9 PT/ES */ + { 0x00E2, 0x015F, 0x01CE, 0x00EE }, /* A RO */ + { 0x0107, 0x017E, 0x0111, 0x0161 }, /* B SR/HR/SL */ + { 0x00E4, 0x00F6, 0x00E5, 0x00FC }, /* C SV/FI/HU */ + { 0x015F, 0x00F6, 0x00E7, 0x00FC }, /* D TR */ +}; +const unsigned short int arrowtable[] = +{ + 8592, 8594, 8593, 8595, 'O', 'K', 8592, 8592 +}; + +CTeletextDecoder::CTeletextDecoder() +{ + memset(&m_RenderInfo, 0, sizeof(TextRenderInfo_t)); + + m_teletextFont = _P(TeletextFont); + m_TextureBuffer = NULL; + m_txtCache = NULL; + m_Manager = NULL; + m_Library = NULL; + m_RenderInfo.ShowFlof = true; + m_RenderInfo.Show39 = true; + m_RenderInfo.Showl25 = true; + m_RenderInfo.Prev_100 = 0x100; + m_RenderInfo.Prev_10 = 0x100; + m_RenderInfo.Next_100 = 0x100; + m_RenderInfo.Next_10 = 0x100; + m_RenderInfo.InputCounter = 2; + + 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 }; + 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 }; + 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 }; + unsigned short tr0[] = {0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, + 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, + 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, + 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, + 0x0000 , 0x0000 , 0x0A0A , 0xFFFF, 0x3030 }; + + memcpy(m_RenderInfo.rd0,rd0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short)); + memcpy(m_RenderInfo.gn0,gn0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short)); + memcpy(m_RenderInfo.bl0,bl0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short)); + memcpy(m_RenderInfo.tr0,tr0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short)); +} + +CTeletextDecoder::~CTeletextDecoder() +{ +} + +bool CTeletextDecoder::HandleAction(const CAction &action) +{ + if (m_txtCache == NULL) + { + CLog::Log(LOGERROR, "CTeletextDecoder::HandleAction called without teletext cache"); + return false; + } + + if (action.id == ACTION_MOVE_UP) + { + if (m_RenderInfo.PageCatching) + CatchNextPage(-1, -1); + else + GetNextPageOne(true); + return true; + } + else if (action.id == ACTION_MOVE_DOWN) + { + if (m_RenderInfo.PageCatching) + CatchNextPage(1, 1); + else + GetNextPageOne(false); + return true; + } + else if (action.id == ACTION_MOVE_RIGHT) + { + if (m_RenderInfo.PageCatching) + CatchNextPage(0, 1); + else if (m_RenderInfo.Boxed) + { + m_RenderInfo.SubtitleDelay++; + // display SubtitleDelay + m_RenderInfo.PosY = 0; + char ns[10]; + SetPosX(1); + sprintf(ns,"+%d ", m_RenderInfo.SubtitleDelay); + RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]); + RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]); + RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]); + RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]); + } + else + { + GetNextSubPage(1); + } + return true; + } + else if (action.id == ACTION_MOVE_LEFT) + { + if (m_RenderInfo.PageCatching) + CatchNextPage(0, -1); + else if (m_RenderInfo.Boxed) + { + m_RenderInfo.SubtitleDelay--; + if (m_RenderInfo.SubtitleDelay < 0) m_RenderInfo.SubtitleDelay = 0; + // display subtitledelay + m_RenderInfo.PosY = 0; + char ns[10]; + SetPosX(1); + sprintf(ns,"+%d ", m_RenderInfo.SubtitleDelay); + RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]); + RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]); + RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]); + RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]); + } + else + { + GetNextSubPage(-1); + } + return true; + } + else if (action.id >= REMOTE_0 && action.id <= REMOTE_9) + { + PageInput(action.id - REMOTE_0); + return true; + } + else if (action.id >= KEY_ASCII) // FIXME make it KEY_UNICODE + { // input from the keyboard + if (action.unicode >= 48 && action.unicode < 58) + { + PageInput(action.unicode - 48); + return true; + } + return false; + } + else if (action.id == ACTION_PAGE_UP) + { + SwitchZoomMode(); + return true; + } + else if (action.id == ACTION_PAGE_DOWN) + { + SwitchTranspMode(); + return true; + } + else if (action.id == ACTION_SELECT_ITEM) + { + if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xFF) + return false; + + if (!m_RenderInfo.PageCatching) + StartPageCatching(); + else + StopPageCatching(); + + return true; + } + + if (m_RenderInfo.PageCatching) + { + m_txtCache->PageUpdate = true; + m_RenderInfo.PageCatching = 0; + return true; + } + + if (action.id == ACTION_SHOW_INFO) + { + SwitchHintMode(); + return true; + } + else if (action.id == ACTION_TELETEXT_RED) + { + ColorKey(m_RenderInfo.Prev_100); + return true; + } + else if (action.id == ACTION_TELETEXT_GREEN) + { + ColorKey(m_RenderInfo.Prev_10); + return true; + } + else if (action.id == ACTION_TELETEXT_YELLOW) + { + ColorKey(m_RenderInfo.Next_10); + return true; + } + else if (action.id == ACTION_TELETEXT_BLUE) + { + ColorKey(m_RenderInfo.Next_100); + return true; + } + + return false; +} + +bool CTeletextDecoder::InitDecoder() +{ + int error; + + m_txtCache = g_application.m_pPlayer->GetTeletextCache(); + if (m_txtCache == NULL) + { + CLog::Log(LOGERROR, "%s: called without teletext cache", __FUNCTION__); + return false; + } + + /* init fontlibrary */ + if ((error = FT_Init_FreeType(&m_Library))) + { + CLog::Log(LOGERROR, "%s: <FT_Init_FreeType: 0x%.2X>", __FUNCTION__, error); + m_Library = NULL; + return false; + } + + if ((error = FTC_Manager_New(m_Library, 7, 2, 0, &MyFaceRequester, NULL, &m_Manager))) + { + FT_Done_FreeType(m_Library); + m_Library = NULL; + m_Manager = NULL; + CLog::Log(LOGERROR, "%s: <FTC_Manager_New: 0x%.2X>", __FUNCTION__, error); + return false; + } + + if ((error = FTC_SBitCache_New(m_Manager, &m_Cache))) + { + FTC_Manager_Done(m_Manager); + FT_Done_FreeType(m_Library); + m_Manager = NULL; + m_Library = NULL; + CLog::Log(LOGERROR, "%s: <FTC_SBit_Cache_New: 0x%.2X>", __FUNCTION__, error); + return false; + } + + /* calculate font dimensions */ + m_RenderInfo.Width = g_graphicsContext.GetWidth()*g_graphicsContext.GetGUIScaleX(); + m_RenderInfo.Height = g_graphicsContext.GetHeight()*g_graphicsContext.GetGUIScaleY(); + m_RenderInfo.FontHeight = m_RenderInfo.Height / 25; + m_RenderInfo.FontWidth_Normal = m_RenderInfo.Width / (m_RenderInfo.Show39 ? 39 : 40); + SetFontWidth(m_RenderInfo.FontWidth_Normal); + for (int i = 0; i <= 10; i++) + m_RenderInfo.axdrcs[i+12+1] = (m_RenderInfo.FontHeight * i + 6) / 10; + + /* center screen */ + m_TypeTTF.face_id = (FTC_FaceID) m_teletextFont.c_str(); + m_TypeTTF.height = (FT_UShort) m_RenderInfo.FontHeight; + m_TypeTTF.flags = FT_LOAD_MONOCHROME; + if ((error = FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face))) + { + m_TypeTTF.face_id = (FTC_FaceID) m_teletextFont.c_str(); + if ((error = FTC_Manager_Lookup_Face(m_Manager, m_TypeTTF.face_id, &m_Face))) + { + CLog::Log(LOGERROR, "%s: <FTC_Manager_Lookup_Face failed with Errorcode 0x%.2X>\n", __FUNCTION__, error); + FTC_Manager_Done(m_Manager); + FT_Done_FreeType(m_Library); + m_Manager = NULL; + m_Library = NULL; + return false; + } + } + m_Ascender = m_RenderInfo.FontHeight * m_Face->ascender / m_Face->units_per_EM; + + /* set variable screeninfo for double buffering */ + m_YOffset = 0; + m_TextureBuffer = new color_t [4*m_RenderInfo.Height*m_RenderInfo.Width]; + + ClearFB(GetColorRGB(TXT_ColorTransp)); + ClearBB(GetColorRGB(TXT_ColorTransp)); /* initialize backbuffer */ + /* set new colormap */ + SetColors((unsigned short *)DefaultColors, 0, TXT_Color_SIZECOLTABLE); + + for (int i = 0; i < 40 * 25; i++) + { + m_RenderInfo.PageChar[i] = ' '; + m_RenderInfo.PageAtrb[i].fg = TXT_ColorTransp; + m_RenderInfo.PageAtrb[i].bg = TXT_ColorTransp; + m_RenderInfo.PageAtrb[i].charset = C_G0P; + m_RenderInfo.PageAtrb[i].doubleh = 0; + m_RenderInfo.PageAtrb[i].doublew = 0; + m_RenderInfo.PageAtrb[i].IgnoreAtBlackBgSubst = 0; + } + + m_RenderInfo.TranspMode = false; + m_LastPage = 0x100; + + return true; +} + +void CTeletextDecoder::EndDecoder() +{ + /* clear SubtitleCache */ + for (int i = 0; i < SUBTITLE_CACHESIZE; i++) + { + if (m_RenderInfo.SubtitleCache[i] != NULL) + { + delete m_RenderInfo.SubtitleCache[i]; + m_RenderInfo.SubtitleCache[i] = NULL; + } + } + + if (m_TextureBuffer) + { + delete[] m_TextureBuffer; + m_TextureBuffer = NULL; + } + + /* close freetype */ + if (m_Manager) + { + FTC_Manager_Done(m_Manager); + } + if (m_Library) + { + FT_Done_FreeType(m_Library); + } + + m_Manager = NULL; + m_Library = NULL; + + if (!m_txtCache) + { + CLog::Log(LOGNOTICE, "%s: called without cache", __FUNCTION__); + } + else + { + m_txtCache->PageUpdate = true; + CLog::Log(LOGDEBUG, "Teletext: Rendering ended"); + } + return; +} + +void CTeletextDecoder::PageInput(int Number) +{ + int zoom = 0; + m_updateTexture = true; + + /* clear m_TempPage */ + if (m_RenderInfo.InputCounter == 2) + m_TempPage = 0; + + /* check for 0 & 9 on first position */ + if (Number == 0 && m_RenderInfo.InputCounter == 2) + { + /* set page */ + m_TempPage = m_LastPage; /* 0 toggles to last page as in program switching */ + m_RenderInfo.InputCounter = -1; + } + else if (Number == 9 && m_RenderInfo.InputCounter == 2) + { + return; + } + + /* show pageinput */ + if (m_RenderInfo.ZoomMode == 2) + { + m_RenderInfo.ZoomMode = 1; + CopyBB2FB(); + } + + if (m_RenderInfo.ZoomMode == 1) + zoom = 1<<10; + + m_RenderInfo.PosY = 0; + + switch (m_RenderInfo.InputCounter) + { + case 2: + SetPosX(1); + RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]); + RenderCharFB('-', &Text_AtrTable[ATR_WB]); + RenderCharFB('-', &Text_AtrTable[ATR_WB]); + break; + + case 1: + SetPosX(2); + RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]); + break; + + case 0: + SetPosX(3); + RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]); + break; + } + + /* generate pagenumber */ + m_TempPage |= Number << m_RenderInfo.InputCounter*4; + + m_RenderInfo.InputCounter--; + + if (m_RenderInfo.InputCounter < 0) + { + /* disable SubPage zapping */ + m_txtCache->ZapSubpageManual = false; + + /* reset input */ + m_RenderInfo.InputCounter = 2; + + /* set new page */ + m_LastPage = m_txtCache->Page; + + m_txtCache->Page = m_TempPage; + m_RenderInfo.HintMode = false; + + /* check cache */ + int subp = m_txtCache->SubPageTable[m_txtCache->Page]; + if (subp != 0xFF) + { + m_txtCache->SubPage = subp; + m_txtCache->PageUpdate = true; + } + else + { + m_txtCache->SubPage = 0; +// RenderMessage(PageNotFound); + } + } +} + +void CTeletextDecoder::GetNextPageOne(bool up) +{ + /* disable subpage zapping */ + m_txtCache->ZapSubpageManual = false; + + /* abort pageinput */ + m_RenderInfo.InputCounter = 2; + + /* find next cached page */ + m_LastPage = m_txtCache->Page; + + int subp; + do { + if (up) + CDVDTeletextTools::NextDec(&m_txtCache->Page); + else + CDVDTeletextTools::PrevDec(&m_txtCache->Page); + subp = m_txtCache->SubPageTable[m_txtCache->Page]; + } while (subp == 0xFF && m_txtCache->Page != m_LastPage); + + /* update Page */ + if (m_txtCache->Page != m_LastPage) + { + if (m_RenderInfo.ZoomMode == 2) + m_RenderInfo.ZoomMode = 1; + + m_txtCache->SubPage = subp; + m_RenderInfo.HintMode = false; + m_txtCache->PageUpdate = true; + } +} + +void CTeletextDecoder::GetNextSubPage(int offset) +{ + /* abort pageinput */ + m_RenderInfo.InputCounter = 2; + + for (int loop = m_txtCache->SubPage + offset; loop != m_txtCache->SubPage; loop += offset) + { + if (loop < 0) + loop = 0x79; + else if (loop > 0x79) + loop = 0; + if (loop == m_txtCache->SubPage) + break; + + if (m_txtCache->astCachetable[m_txtCache->Page][loop]) + { + /* enable manual SubPage zapping */ + m_txtCache->ZapSubpageManual = true; + + /* update page */ + if (m_RenderInfo.ZoomMode == 2) /* if zoomed to lower half */ + m_RenderInfo.ZoomMode = 1; /* activate upper half */ + + m_txtCache->SubPage = loop; + m_RenderInfo.HintMode = false; + m_txtCache->PageUpdate = true; + + return; + } + } +} + +void CTeletextDecoder::SwitchZoomMode() +{ + if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF) + { + /* toggle mode */ + m_RenderInfo.ZoomMode++; + + if (m_RenderInfo.ZoomMode == 3) + m_RenderInfo.ZoomMode = 0; + + /* update page */ + m_txtCache->PageUpdate = true; + } +} + +void CTeletextDecoder::SwitchTranspMode() +{ + /* toggle mode */ + if (!m_RenderInfo.TranspMode) + m_RenderInfo.TranspMode = true; + else + m_RenderInfo.TranspMode = false; /* backward to immediately switch to TV-screen */ + + /* set mode */ + if (!m_RenderInfo.TranspMode) /* normal text-only */ + { + ClearBB(m_txtCache->FullScrColor); + m_txtCache->PageUpdate = true; + } + else /* semi-transparent BG with FG text */ + { + ClearBB(TXT_ColorTransp); + m_txtCache->PageUpdate = true; + } +} + +void CTeletextDecoder::SwitchHintMode() +{ + /* toggle mode */ + m_RenderInfo.HintMode ^= true; + + if (!m_RenderInfo.HintMode) /* toggle evaluation of level 2.5 information by explicitly switching off HintMode */ + { + m_RenderInfo.Showl25 ^= true; + } + /* update page */ + m_txtCache->PageUpdate = true; +} + +void CTeletextDecoder::ColorKey(int target) +{ + if (!target) + return; + + if (m_RenderInfo.ZoomMode == 2) + m_RenderInfo.ZoomMode = 1; + + m_LastPage = m_txtCache->Page; + m_txtCache->Page = target; + m_txtCache->SubPage = m_txtCache->SubPageTable[m_txtCache->Page]; + m_RenderInfo.InputCounter = 2; + m_RenderInfo.HintMode = false; + m_txtCache->PageUpdate = true; +} + +void CTeletextDecoder::StartPageCatching() +{ + m_RenderInfo.PageCatching = true; + + /* abort pageinput */ + m_RenderInfo.InputCounter = 2; + + /* show info line */ + m_RenderInfo.ZoomMode = 0; + m_RenderInfo.PosX = 0; + m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight; + + /* check for pagenumber(s) */ + m_CatchRow = 1; + m_CatchCol = 0; + m_CatchedPage = 0; + m_PCOldRow = 0; + m_PCOldCol = 0; /* no inverted page number to restore yet */ + CatchNextPage(0, 1); + + if (!m_CatchedPage) + { + m_RenderInfo.PageCatching = false; + m_txtCache->PageUpdate = true; + return; + } +} + +void CTeletextDecoder::StopPageCatching() +{ + /* set new page */ + if (m_RenderInfo.ZoomMode == 2) + m_RenderInfo.ZoomMode = 1; + + m_LastPage = m_txtCache->Page; + m_txtCache->Page = m_CatchedPage; + m_RenderInfo.HintMode = false; + m_txtCache->PageUpdate = true; + m_RenderInfo.PageCatching = false; + + int subp = m_txtCache->SubPageTable[m_txtCache->Page]; + if (subp != 0xFF) + m_txtCache->SubPage = subp; + else + m_txtCache->SubPage = 0; +} + +void CTeletextDecoder::CatchNextPage(int firstlineinc, int inc) +{ + int tmp_page, allowwrap = 1; /* allow first wrap around */ + + /* catch next page */ + for(;;) + { + unsigned char *p = &(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol]); + TextPageAttr_t a = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol]; + + if (!(a.charset == C_G1C || a.charset == C_G1S) && /* no mosaic */ + (a.fg != a.bg) && /* not hidden */ + (*p >= '1' && *p <= '8' && /* valid page number */ + *(p+1) >= '0' && *(p+1) <= '9' && + *(p+2) >= '0' && *(p+2) <= '9') && + (m_CatchRow == 0 || (*(p-1) < '0' || *(p-1) > '9')) && /* non-numeric char before and behind */ + (m_CatchRow == 37 || (*(p+3) < '0' || *(p+3) > '9'))) + { + tmp_page = ((*p - '0')<<8) | ((*(p+1) - '0')<<4) | (*(p+2) - '0'); + +#if 0 + if (tmp_page != m_CatchedPage) /* confusing to skip identical page numbers - I want to reach what I aim to */ +#endif + { + m_CatchedPage = tmp_page; + RenderCatchedPage(); + m_CatchCol += inc; /* FIXME: limit */ + return; + } + } + + if (firstlineinc > 0) + { + m_CatchRow++; + m_CatchCol = 0; + firstlineinc = 0; + } + else if (firstlineinc < 0) + { + m_CatchRow--; + m_CatchCol = 37; + firstlineinc = 0; + } + else + m_CatchCol += inc; + + if (m_CatchCol > 37) + { + m_CatchRow++; + m_CatchCol = 0; + } + else if (m_CatchCol < 0) + { + m_CatchRow--; + m_CatchCol = 37; + } + + if (m_CatchRow > 23) + { + if (allowwrap) + { + allowwrap = 0; + m_CatchRow = 1; + m_CatchCol = 0; + } + else + { + return; + } + } + else if (m_CatchRow < 1) + { + if (allowwrap) + { + allowwrap = 0; + m_CatchRow = 23; + m_CatchCol =37; + } + else + { + return; + } + } + } +} + +void CTeletextDecoder::RenderCatchedPage() +{ + int zoom = 0; + m_updateTexture = true; + + /* handle zoom */ + if (m_RenderInfo.ZoomMode) + zoom = 1<<10; + + if (m_PCOldRow || m_PCOldCol) /* not at first call */ + { + /* restore pagenumber */ + SetPosX(m_PCOldCol); + + if (m_RenderInfo.ZoomMode == 2) + m_RenderInfo.PosY = (m_PCOldRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1); + else + m_RenderInfo.PosY = m_PCOldRow*m_RenderInfo.FontHeight*((zoom>>10)+1); + + RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol ], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol ]); + RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 1], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 1]); + RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 2], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 2]); + } + + m_PCOldRow = m_CatchRow; + m_PCOldCol = m_CatchCol; + + /* mark pagenumber */ + if (m_RenderInfo.ZoomMode == 1 && m_CatchRow > 11) + { + m_RenderInfo.ZoomMode = 2; + CopyBB2FB(); + } + else if (m_RenderInfo.ZoomMode == 2 && m_CatchRow < 12) + { + m_RenderInfo.ZoomMode = 1; + CopyBB2FB(); + } + SetPosX(m_CatchCol); + + if (m_RenderInfo.ZoomMode == 2) + m_RenderInfo.PosY = (m_CatchRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1); + else + m_RenderInfo.PosY = m_CatchRow*m_RenderInfo.FontHeight*((zoom>>10)+1); + + TextPageAttr_t a0 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol ]; + TextPageAttr_t a1 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 1]; + TextPageAttr_t a2 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 2]; + int t; + + /* exchange colors */ + t = a0.fg; a0.fg = a0.bg; a0.bg = t; + t = a1.fg; a1.fg = a1.bg; a1.bg = t; + t = a2.fg; a2.fg = a2.bg; a2.bg = t; + + RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol ], &a0); + RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 1], &a1); + RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 2], &a2); +} + +void CTeletextDecoder::RenderPage() +{ + int StartRow = 0; + int national_subset_bak = m_txtCache->NationalSubset; + + if (m_txtCache->PageUpdate) + m_updateTexture = true; + + /* update page or timestring */ + if (m_txtCache->PageUpdate && m_txtCache->PageReceiving != m_txtCache->Page && m_RenderInfo.InputCounter == 2) + { + /* reset update flag */ + m_txtCache->PageUpdate = false; + if (m_RenderInfo.Boxed && m_RenderInfo.SubtitleDelay) + { + TextSubtitleCache_t* c = NULL; + int j = -1; + for (int i = 0; i < SUBTITLE_CACHESIZE; i++) + { + if (j == -1 && !m_RenderInfo.SubtitleCache[i]) + j = i; + if (m_RenderInfo.SubtitleCache[i] && !m_RenderInfo.SubtitleCache[i]->Valid) + { + c = m_RenderInfo.SubtitleCache[i]; + break; + } + } + if (c == NULL) + { + if (j == -1) // no more space in SubtitleCache + return; + + c = new TextSubtitleCache_t; + if (c == NULL) + return; + + memset(c, 0x00, sizeof(TextSubtitleCache_t)); + m_RenderInfo.SubtitleCache[j] = c; + } + c->Valid = true; + c->Timestamp = timeGetTime()/1000; + + if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF) + { + TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, c->PageChar, c->PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof); + if (p) + { + m_RenderInfo.Boxed = p->boxed; + } + } + m_RenderInfo.DelayStarted = true; + return; + } + m_RenderInfo.DelayStarted = false; + /* decode page */ + if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF) + { + TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, m_RenderInfo.PageChar, m_RenderInfo.PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof); + if (p) + { + m_RenderInfo.PageInfo = p; + m_RenderInfo.Boxed = p->boxed; + } + if (m_RenderInfo.Boxed || m_RenderInfo.TranspMode) + FillBorder(GetColorRGB(TXT_ColorTransp)); + else + FillBorder(GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor)); + + if (m_txtCache->ColorTable) /* as late as possible to shorten the time the old page is displayed with the new colors */ + SetColors(m_txtCache->ColorTable, 16, 16); /* set colors for CLUTs 2+3 */ + } + else + StartRow = 1; + + DoRenderPage(StartRow, national_subset_bak); + } + else + { + if (m_RenderInfo.DelayStarted) + { + long now = timeGetTime()/1000; + for (int i = 0; i < SUBTITLE_CACHESIZE ; i++) + { + if (m_RenderInfo.SubtitleCache[i] && m_RenderInfo.SubtitleCache[i]->Valid && now - m_RenderInfo.SubtitleCache[i]->Timestamp >= m_RenderInfo.SubtitleDelay) + { + memcpy(m_RenderInfo.PageChar, m_RenderInfo.SubtitleCache[i]->PageChar, 40 * 25); + memcpy(m_RenderInfo.PageAtrb, m_RenderInfo.SubtitleCache[i]->PageAtrb, 40 * 25 * sizeof(TextPageAttr_t)); + DoRenderPage(StartRow, national_subset_bak); + m_RenderInfo.SubtitleCache[i]->Valid = false; + return; + } + } + } + if (m_RenderInfo.ZoomMode != 2) + { + m_RenderInfo.PosY = 0; + if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff) + { + m_RenderInfo.PageAtrb[32].fg = TXT_ColorYellow; + m_RenderInfo.PageAtrb[32].bg = TXT_ColorMenu1; + int showpage = m_txtCache->PageReceiving; + int showsubpage = m_txtCache->SubPageTable[showpage]; + if (showsubpage!=0xff) + { + TextCachedPage_t *pCachedPage; + pCachedPage = m_txtCache->astCachetable[showpage][showsubpage]; + if (pCachedPage && IsDec(showpage)) + { + m_RenderInfo.PosX = 0; + if (m_RenderInfo.InputCounter == 2) + { + if (m_txtCache->BTTok && !m_txtCache->BasicTop[m_txtCache->Page]) /* page non-existent according to TOP (continue search anyway) */ + { + m_RenderInfo.PageAtrb[0].fg = TXT_ColorWhite; + m_RenderInfo.PageAtrb[0].bg = TXT_ColorRed; + } + else + { + m_RenderInfo.PageAtrb[0].fg = TXT_ColorYellow; + m_RenderInfo.PageAtrb[0].bg = TXT_ColorMenu1; + } + CDVDTeletextTools::Hex2Str((char*)m_RenderInfo.PageChar+3, m_txtCache->Page); + + int col; + for (col = m_RenderInfo.nofirst; col < 7; col++) // selected page + { + RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[0]); + } + RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[32]); + } + else + SetPosX(8); + + memcpy(&m_RenderInfo.PageChar[8], pCachedPage->p0, 24); /* header line without timestring */ + for (int i = 0; i < 24; i++) + { + RenderCharFB(pCachedPage->p0[i], &m_RenderInfo.PageAtrb[32]); + } + + /* Update on every Header number change */ + if (pCachedPage->p0[2] != prevHeaderPage) + { + prevHeaderPage = pCachedPage->p0[2]; + m_updateTexture = true; + } + } + } + } + + /* update timestring */ + SetPosX(32); + for (int i = 0; i < 8; i++) + { + if (!m_RenderInfo.PageAtrb[32+i].flashing) + RenderCharFB(m_txtCache->TimeString[i], &m_RenderInfo.PageAtrb[32]); + else + { + SetPosX(33+i); + m_RenderInfo.PageChar[32+i] = m_RenderInfo.PageChar[32+i]; + } + } + + /* Update on every changed second */ + if (m_txtCache->TimeString[7] != prevTimeSec) + { + prevTimeSec = m_txtCache->TimeString[7]; + m_updateTexture = true; + } + } + DoFlashing(StartRow); + m_txtCache->NationalSubset = national_subset_bak; + } +} + +void CTeletextDecoder::DoFlashing(int startrow) +{ + /* get national subset */ + if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */ + m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */ + { + m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national]; + } + + /* Flashing */ + TextPageAttr_t flashattr; + char flashchar; + long flashphase = timeGetTime() % 1000; + + int srow = startrow; + int erow = 24; + int factor=1; + + switch (m_RenderInfo.ZoomMode) + { + case 1: erow = 12; factor=2;break; + case 2: srow = 12; factor=2;break; + } + + m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight*factor; + for (int row = srow; row < erow; row++) + { + int index = row * 40; + int dhset = 0; + int incflash = 3; + int decflash = 2; + + m_RenderInfo.PosX = 0; + for (int col = m_RenderInfo.nofirst; col < 40; col++) + { + if (m_RenderInfo.PageAtrb[index + col].flashing && m_RenderInfo.PageChar[index + col] > 0x20 && m_RenderInfo.PageChar[index + col] != 0xff ) + { + SetPosX(col); + flashchar = m_RenderInfo.PageChar[index + col]; + bool doflash = false; + memcpy(&flashattr, &m_RenderInfo.PageAtrb[index + col], sizeof(TextPageAttr_t)); + switch (flashattr.flashing &0x1c) // Flash Rate + { + case 0x00 : // 1 Hz + if (flashphase>500) doflash = true; + break; + case 0x04 : // 2 Hz Phase 1 + if (flashphase<250) doflash = true; + break; + case 0x08 : // 2 Hz Phase 2 + if (flashphase>=250 && flashphase<500) doflash = true; + break; + case 0x0c : // 2 Hz Phase 3 + if (flashphase>=500 && flashphase<750) doflash = true; + break; + case 0x10 : // incremental flash + incflash++; + if (incflash>3) incflash = 1; + switch (incflash) + { + case 1: if (flashphase<250) doflash = true; break; + case 2: if (flashphase>=250 && flashphase<500) doflash = true;break; + case 3: if (flashphase>=500 && flashphase<750) doflash = true; + } + break; + case 0x11 : // decremental flash + decflash--; + if (decflash<1) decflash = 3; + switch (decflash) + { + case 1: if (flashphase<250) doflash = true; break; + case 2: if (flashphase>=250 && flashphase<500) doflash = true;break; + case 3: if (flashphase>=500 && flashphase<750) doflash = true; + } + break; + + } + + switch (flashattr.flashing &0x03) // Flash Mode + { + case 0x01 : // normal Flashing + if (doflash) flashattr.fg = flashattr.bg; + break; + case 0x02 : // inverted Flashing + doflash = !doflash; + if (doflash) flashattr.fg = flashattr.bg; + break; + case 0x03 : // color Flashing + if (doflash) flashattr.fg = flashattr.fg + (flashattr.fg > 7 ? (-8) : 8); + break; + + } + RenderCharFB(flashchar, &flashattr); + if (flashattr.doublew) col++; + if (flashattr.doubleh) dhset = 1; + + m_updateTexture = true; + } + } + if (dhset) + { + row++; + m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor; + } + m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor; + } +} + +void CTeletextDecoder::DoRenderPage(int startrow, int national_subset_bak) +{ + /* display first column? */ + m_RenderInfo.nofirst = m_RenderInfo.Show39; + for (int row = 1; row < 24; row++) + { + int Byte = m_RenderInfo.PageChar[row*40]; + if (Byte != ' ' && Byte != 0x00 && Byte != 0xFF && m_RenderInfo.PageAtrb[row*40].fg != m_RenderInfo.PageAtrb[row*40].bg) + { + m_RenderInfo.nofirst = 0; + break; + } + } + + if (m_RenderInfo.TranspMode || m_RenderInfo.Boxed) + { + FillBorder(GetColorRGB(TXT_ColorTransp));//ClearBB(transp); + m_RenderInfo.ClearBBColor = TXT_ColorTransp; + } + + /* get national subset */ + if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */ + m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */ + { + m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national]; + } + /* render page */ + if (m_RenderInfo.PageInfo && (m_RenderInfo.PageInfo->function == FUNC_GDRCS || m_RenderInfo.PageInfo->function == FUNC_DRCS)) /* character definitions */ + { + #define DRCSROWS 8 + #define DRCSCOLS (48/DRCSROWS) + #define DRCSZOOMX 3 + #define DRCSZOOMY 5 + #define DRCSXSPC (12*DRCSZOOMX + 2) + #define DRCSYSPC (10*DRCSZOOMY + 2) + + unsigned char ax[] = { /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */ + DRCSZOOMX * 0, + DRCSZOOMX * 1, + DRCSZOOMX * 2, + DRCSZOOMX * 3, + DRCSZOOMX * 4, + DRCSZOOMX * 5, + DRCSZOOMX * 6, + DRCSZOOMX * 7, + DRCSZOOMX * 8, + DRCSZOOMX * 9, + DRCSZOOMX * 10, + DRCSZOOMX * 11, + DRCSZOOMX * 12, + DRCSZOOMY * 0, + DRCSZOOMY * 1, + DRCSZOOMY * 2, + DRCSZOOMY * 3, + DRCSZOOMY * 4, + DRCSZOOMY * 5, + DRCSZOOMY * 6, + DRCSZOOMY * 7, + DRCSZOOMY * 8, + DRCSZOOMY * 9, + DRCSZOOMY * 10 + }; + + ClearBB(TXT_ColorBlack); + for (int col = 0; col < 24*40; col++) + m_RenderInfo.PageAtrb[col] = Text_AtrTable[ATR_WB]; + + for (int row = 0; row < DRCSROWS; row++) + { + for (int col = 0; col < DRCSCOLS; col++) + { + RenderDRCS(m_RenderInfo.Width, + m_RenderInfo.PageChar + 20 * (DRCSCOLS * row + col + 2), + m_TextureBuffer + + (m_RenderInfo.FontHeight + DRCSYSPC * row + m_RenderInfo.Height) * m_RenderInfo.Width + + DRCSXSPC * col, + ax, GetColorRGB(TXT_ColorWhite), GetColorRGB(TXT_ColorBlack)); + } + } + memset(m_RenderInfo.PageChar + 40, 0xff, 24*40); /* don't render any char below row 0 */ + } + m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight; + for (int row = startrow; row < 24; row++) + { + int index = row * 40; + + m_RenderInfo.PosX = 0; + for (int col = m_RenderInfo.nofirst; col < 40; col++) + { + RenderCharBB(m_RenderInfo.PageChar[index + col], &m_RenderInfo.PageAtrb[index + col]); + + if (m_RenderInfo.PageAtrb[index + col].doubleh && m_RenderInfo.PageChar[index + col] != 0xff) /* disable lower char in case of doubleh setting in l25 objects */ + m_RenderInfo.PageChar[index + col + 40] = 0xff; + if (m_RenderInfo.PageAtrb[index + col].doublew) /* skip next column if double width */ + { + col++; + 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 */ + m_RenderInfo.PageChar[index + col + 40] = 0xff; + } + } + m_RenderInfo.PosY += m_RenderInfo.FontHeight; + } + DoFlashing(startrow); + + /* update framebuffer */ + CopyBB2FB(); + m_txtCache->NationalSubset = national_subset_bak; +} + +void CTeletextDecoder::Decode_BTT() +{ + /* basic top table */ + int current, b1, b2, b3, b4; + unsigned char btt[23*40]; + + if (m_txtCache->SubPageTable[0x1f0] == 0xff || 0 == m_txtCache->astCachetable[0x1f0][m_txtCache->SubPageTable[0x1f0]]) /* not yet received */ + return; + + g_application.m_pPlayer->LoadPage(0x1f0, m_txtCache->SubPageTable[0x1f0],btt); + if (btt[799] == ' ') /* not completely received or error */ + return; + + current = 0x100; + for (int i = 0; i < 800; i++) + { + b1 = btt[i]; + if (b1 == ' ') + b1 = 0; + else + { + b1 = dehamming[b1]; + if (b1 == 0xFF) /* hamming error in btt */ + { + btt[799] = ' '; /* mark btt as not received */ + return; + } + } + m_txtCache->BasicTop[current] = b1; + CDVDTeletextTools::NextDec(¤t); + } + /* page linking table */ + m_txtCache->ADIP_PgMax = -1; /* rebuild table of adip pages */ + for (int i = 0; i < 10; i++) + { + b1 = dehamming[btt[800 + 8*i +0]]; + + if (b1 == 0xE) + continue; /* unused */ + else if (b1 == 0xF) + break; /* end */ + + b4 = dehamming[btt[800 + 8*i +7]]; + + if (b4 != 2) /* only adip, ignore multipage (1) */ + continue; + + b2 = dehamming[btt[800 + 8*i +1]]; + b3 = dehamming[btt[800 + 8*i +2]]; + + if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF) + { + CLog::Log(LOGERROR, "CTeletextDecoder::Decode_BTT <Biterror in btt/plt index %d>", i); + btt[799] = ' '; /* mark btt as not received */ + return; + } + + b1 = b1<<8 | b2<<4 | b3; /* page number */ + m_txtCache->ADIP_Pg[++m_txtCache->ADIP_PgMax] = b1; + } + + m_txtCache->BTTok = true; +} + +void CTeletextDecoder::Decode_ADIP() /* additional information table */ +{ + int i, p, j, b1, b2, b3, charfound; + unsigned char padip[23*40]; + + for (i = 0; i <= m_txtCache->ADIP_PgMax; i++) + { + p = m_txtCache->ADIP_Pg[i]; + if (!p || m_txtCache->SubPageTable[p] == 0xff || 0 == m_txtCache->astCachetable[p][m_txtCache->SubPageTable[p]]) /* not cached (avoid segfault) */ + continue; + + g_application.m_pPlayer->LoadPage(p,m_txtCache->SubPageTable[p],padip); + for (j = 0; j < 44; j++) + { + b1 = dehamming[padip[20*j+0]]; + if (b1 == 0xE) + continue; /* unused */ + + if (b1 == 0xF) + break; /* end */ + + b2 = dehamming[padip[20*j+1]]; + b3 = dehamming[padip[20*j+2]]; + + if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF) + { + CLog::Log(LOGERROR, "CTeletextDecoder::Decode_BTT <Biterror in ait %03x %d %02x %02x %02x %02x %02x %02x>", p, j, + padip[20*j+0], + padip[20*j+1], + padip[20*j+2], + b1, b2, b3 + ); + return; + } + + if (b1>8 || b2>9 || b3>9) /* ignore extries with invalid or hex page numbers */ + { + continue; + } + + b1 = b1<<8 | b2<<4 | b3; /* page number */ + charfound = 0; /* flag: no printable char found */ + + for (b2 = 11; b2 >= 0; b2--) + { + b3 = deparity[padip[20*j + 8 + b2]]; + if (b3 < ' ') + b3 = ' '; + + if (b3 == ' ' && !charfound) + m_txtCache->ADIPTable[b1][b2] = '\0'; + else + { + m_txtCache->ADIPTable[b1][b2] = b3; + charfound = 1; + } + } + } /* next link j */ + + m_txtCache->ADIP_Pg[i] = 0; /* completely decoded: clear entry */ + } /* next adip page i */ + + while (!m_txtCache->ADIP_Pg[m_txtCache->ADIP_PgMax] && (m_txtCache->ADIP_PgMax >= 0)) /* and shrink table */ + m_txtCache->ADIP_PgMax--; +} + +int CTeletextDecoder::TopText_GetNext(int startpage, int up, int findgroup) +{ + int current, nextgrp, nextblk; + + int stoppage = (IsDec(startpage) ? startpage : startpage & 0xF00); // avoid endless loop in hexmode + nextgrp = nextblk = 0; + current = startpage; + + do { + if (up) + CDVDTeletextTools::NextDec(¤t); + else + CDVDTeletextTools::PrevDec(¤t); + + if (!m_txtCache->BTTok || m_txtCache->BasicTop[current]) /* only if existent */ + { + if (findgroup) + { + if (m_txtCache->BasicTop[current] >= 6 && m_txtCache->BasicTop[current] <= 7) + return current; + if (!nextgrp && (current&0x00F) == 0) + nextgrp = current; + } + if (m_txtCache->BasicTop[current] >= 2 && m_txtCache->BasicTop[current] <= 5) /* always find block */ + return current; + + if (!nextblk && (current&0x0FF) == 0) + nextblk = current; + } + } while (current != stoppage); + + if (nextgrp) + return nextgrp; + else if (nextblk) + return nextblk; + else + return current; +} + +void CTeletextDecoder::Showlink(int column, int linkpage) +{ + unsigned char line[] = " >??? "; + int oldfontwidth = m_RenderInfo.FontWidth; + int yoffset; + + if (m_YOffset) + yoffset = 0; + else + yoffset = m_RenderInfo.Height; + + 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 + int width = m_RenderInfo.Width /4; + + m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight; + + if (m_RenderInfo.Boxed) + { + m_RenderInfo.PosX = column*width; + FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, m_RenderInfo.Width, m_RenderInfo.FontHeight, GetColorRGB(TXT_ColorTransp)); + return; + } + + if (m_txtCache->ADIPTable[linkpage][0]) + { + m_RenderInfo.PosX = column*width; + int l = strlen(m_txtCache->ADIPTable[linkpage]); + + if (l > 9) /* smaller font, if no space for one half space at front and end */ + SetFontWidth(oldfontwidth * 10 / (l+1)); + + 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)); + m_RenderInfo.PosX += ((width) - (l*m_RenderInfo.FontWidth+l*m_RenderInfo.FontWidth/abx))/2; /* center */ + + for (char *p = m_txtCache->ADIPTable[linkpage]; *p; p++) + RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]); + + SetFontWidth(oldfontwidth); + } + else /* display number */ + { + m_RenderInfo.PosX = column*width; + 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)); + if (linkpage < m_txtCache->Page) + { + line[6] = '<'; + CDVDTeletextTools::Hex2Str((char*)line + 5, linkpage); + } + else + CDVDTeletextTools::Hex2Str((char*)line + 6, linkpage); + + for (unsigned char *p = line; p < line+9; p++) + RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]); + } +} + +void CTeletextDecoder::CreateLine25() +{ + /* btt completely received and not yet decoded */ + if (!m_txtCache->BTTok) + Decode_BTT(); + + if (m_txtCache->ADIP_PgMax >= 0) + Decode_ADIP(); + + if (!m_RenderInfo.ShowHex && m_RenderInfo.ShowFlof && + (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 + { + m_RenderInfo.Prev_100 = m_txtCache->FlofPages[m_txtCache->Page][0]; + m_RenderInfo.Prev_10 = m_txtCache->FlofPages[m_txtCache->Page][1]; + m_RenderInfo.Next_10 = m_txtCache->FlofPages[m_txtCache->Page][2]; + m_RenderInfo.Next_100 = m_txtCache->FlofPages[m_txtCache->Page][3]; + + m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight; + m_RenderInfo.PosX = 0; + for (int i=m_RenderInfo.nofirst; i<40; i++) + RenderCharBB(m_RenderInfo.PageChar[24*40 + i], &m_RenderInfo.PageAtrb[24*40 + i]); + } + else + { + /* normal: blk-1, grp+1, grp+2, blk+1 */ + /* hex: hex+1, blk-1, grp+1, blk+1 */ + if (m_RenderInfo.ShowHex) + { + /* arguments: startpage, up, findgroup */ + m_RenderInfo.Prev_100 = NextHex(m_txtCache->Page); + m_RenderInfo.Prev_10 = TopText_GetNext(m_txtCache->Page, 0, 0); + m_RenderInfo.Next_10 = TopText_GetNext(m_txtCache->Page, 1, 1); + } + else + { + m_RenderInfo.Prev_100 = TopText_GetNext(m_txtCache->Page, 0, 0); + m_RenderInfo.Prev_10 = TopText_GetNext(m_txtCache->Page, 1, 1); + m_RenderInfo.Next_10 = TopText_GetNext(m_RenderInfo.Prev_10, 1, 1); + } + m_RenderInfo.Next_100 = TopText_GetNext(m_RenderInfo.Next_10, 1, 0); + Showlink(0, m_RenderInfo.Prev_100); + Showlink(1, m_RenderInfo.Prev_10); + Showlink(2, m_RenderInfo.Next_10); + Showlink(3, m_RenderInfo.Next_100); + } +} + +void CTeletextDecoder::RenderCharFB(int Char, TextPageAttr_t *Attribute) +{ + RenderCharIntern(&m_RenderInfo, Char, Attribute, m_RenderInfo.ZoomMode, m_YOffset); +} + +void CTeletextDecoder::RenderCharBB(int Char, TextPageAttr_t *Attribute) +{ + RenderCharIntern(&m_RenderInfo, Char, Attribute, 0, m_RenderInfo.Height-m_YOffset); +} + +void CTeletextDecoder::CopyBB2FB() +{ + color_t *src, *dst, *topsrc; + int screenwidth; + color_t fillcolor; + + /* line 25 */ + if (!m_RenderInfo.PageCatching) + CreateLine25(); + + /* copy backbuffer to framebuffer */ + if (!m_RenderInfo.ZoomMode) + { + if (m_YOffset) + m_YOffset = 0; + else + m_YOffset = m_RenderInfo.Height; + + if (m_RenderInfo.ClearBBColor >= 0) + { + m_RenderInfo.ClearBBColor = -1; + } + return; + } + + src = dst = topsrc = m_TextureBuffer + m_RenderInfo.Width; + + if (m_YOffset) + { + dst += m_RenderInfo.Width * m_RenderInfo.Height; + } + else + { + src += m_RenderInfo.Width * m_RenderInfo.Height; + topsrc += m_RenderInfo.Width * m_RenderInfo.Height; + } + + if (!m_RenderInfo.PageCatching) + 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 */ + + if (m_RenderInfo.TranspMode) + fillcolor = GetColorRGB(TXT_ColorTransp); + else + fillcolor = GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor); + + if (m_RenderInfo.ZoomMode == 2) + src += 12*m_RenderInfo.FontHeight*m_RenderInfo.Width; + + screenwidth = m_RenderInfo.Width; + + for (int i = 12*m_RenderInfo.FontHeight; i; i--) + { + SDL_memcpy4(dst, src, screenwidth); + dst += m_RenderInfo.Width; + SDL_memcpy4(dst, src, screenwidth); + dst += m_RenderInfo.Width; + src += m_RenderInfo.Width; + } + + for (int i = m_RenderInfo.Height - 25*m_RenderInfo.FontHeight; i >= 0;i--) + { + SDL_memset4(dst + m_RenderInfo.Width*(m_RenderInfo.FontHeight+i), fillcolor, screenwidth); + } +} + +FT_Error CTeletextDecoder::MyFaceRequester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *aface) +{ + FT_Error result = FT_New_Face(library, (const char*)face_id, 0, aface); + + if (!result) + CLog::Log(LOGNOTICE, "Teletext font %s loaded", (char*)face_id); + else + CLog::Log(LOGERROR, "Opening of Teletext font %s failed", (char*)face_id); + + return result; +} + +void CTeletextDecoder::SetFontWidth(int newWidth) +{ + if (m_RenderInfo.FontWidth != newWidth) + { + m_RenderInfo.FontWidth = newWidth; + m_TypeTTF.width = (FT_UShort) m_RenderInfo.FontWidth; + + for (int i = 0; i <= 12; i++) + m_RenderInfo.axdrcs[i] = (m_RenderInfo.FontWidth * i + 6) / 12; + } +} + +int CTeletextDecoder::GetCurFontWidth() +{ + int mx = (m_RenderInfo.Width)%(40-m_RenderInfo.nofirst); // # of unused pixels + int abx = (mx == 0 ? m_RenderInfo.Width+1 : (m_RenderInfo.Width)/(mx+1)); // distance between 'inserted' pixels + int nx = abx+1-(m_RenderInfo.PosX % (abx+1)); // # of pixels to next insert + return m_RenderInfo.FontWidth+(((m_RenderInfo.PosX+m_RenderInfo.FontWidth+1) <= m_RenderInfo.Width && nx <= m_RenderInfo.FontWidth+1) ? 1 : 0); +} + +void CTeletextDecoder::SetPosX(int column) +{ + m_RenderInfo.PosX = 0; + + for (int i = 0; i < column-m_RenderInfo.nofirst; i++) + m_RenderInfo.PosX += GetCurFontWidth(); +} + +void CTeletextDecoder::ClearBB(color_t Color) +{ + SDL_memset4(m_TextureBuffer + (m_RenderInfo.Height-m_YOffset)*m_RenderInfo.Width, Color, m_RenderInfo.Width*m_RenderInfo.Height); +} + +void CTeletextDecoder::ClearFB(color_t Color) +{ + SDL_memset4(m_TextureBuffer + m_RenderInfo.Width*m_YOffset, Color, m_RenderInfo.Width*m_RenderInfo.Height); +} + +void CTeletextDecoder::FillBorder(color_t Color) +{ + 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); + 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); +} + +void CTeletextDecoder::FillRect(color_t *buffer, int xres, int x, int y, int w, int h, color_t Color) +{ + if (!buffer) return; + + color_t *p = buffer + x + y * xres; + + if (w > 0) + { + for ( ; h > 0 ; h--) + { + SDL_memset4(p, Color, w); + p += xres; + } + } +} + +void CTeletextDecoder::DrawVLine(color_t *lfb, int xres, int x, int y, int l, color_t color) +{ + if (!lfb) return; + color_t *p = lfb + x + y * xres; + + for ( ; l > 0 ; l--) + { + *p = color; + p += xres; + } +} + +void CTeletextDecoder::DrawHLine(color_t *lfb, int xres,int x, int y, int l, color_t color) +{ + if (!lfb) return; + if (l > 0) + SDL_memset4(lfb + x + y * xres, color, l); +} + +void CTeletextDecoder::RenderDRCS(int xres, + unsigned char *s, /* pointer to char data, parity undecoded */ + color_t *d, /* pointer to frame buffer of top left pixel */ + unsigned char *ax, /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */ + color_t fgcolor, color_t bgcolor) +{ + if (d == NULL) return; + + unsigned char *ay = ax + 13; /* array[0..10] of y-offsets for each pixel */ + + for (int y = 0; y < 10; y++) /* 10*2 bytes a 6 pixels per char definition */ + { + unsigned char c1 = deparity[*s++]; + unsigned char c2 = deparity[*s++]; + int h = ay[y+1] - ay[y]; + + if (!h) + continue; + if (((c1 == ' ') && (*(s-2) != ' ')) || ((c2 == ' ') && (*(s-1) != ' '))) /* parity error: stop decoding FIXME */ + return; + for (int bit = 0x20, x = 0; + bit; + bit >>= 1, x++) /* bit mask (MSB left), column counter */ + { + color_t f1 = (c1 & bit) ? fgcolor : bgcolor; + color_t f2 = (c2 & bit) ? fgcolor : bgcolor; + for (int i = 0; i < h; i++) + { + if (ax[x+1] > ax[x]) + SDL_memset4(d + ax[x], f1, ax[x+1] - ax[x]); + if (ax[x+7] > ax[x+6]) + SDL_memset4(d + ax[x+6], f2, ax[x+7] - ax[x+6]); /* 2nd byte 6 pixels to the right */ + d += xres; + } + d -= h * xres; + } + d += h * xres; + } +} + +void CTeletextDecoder::FillRectMosaicSeparated(color_t *lfb, int xres,int x, int y, int w, int h, color_t fgcolor, color_t bgcolor, int set) +{ + if (!lfb) return; + FillRect(lfb,xres,x, y, w, h, bgcolor); + if (set) + { + FillRect(lfb,xres,x+1, y+1, w-2, h-2, fgcolor); + } +} + +void CTeletextDecoder::FillTrapez(color_t *lfb, int xres,int x0, int y0, int l0, int xoffset1, int h, int l1, color_t color) +{ + color_t *p = lfb + x0 + y0 * xres; + int xoffset, l; + + for (int yoffset = 0; yoffset < h; yoffset++) + { + l = l0 + ((l1-l0) * yoffset + h/2) / h; + xoffset = (xoffset1 * yoffset + h/2) / h; + if (l > 0) + SDL_memset4(p + xoffset, color, l); + p += xres; + } +} + +void CTeletextDecoder::FlipHorz(color_t *lfb, int xres,int x, int y, int w, int h) +{ + color_t buf[2048]; + color_t *p = lfb + x + y * xres; + int w1,h1; + + for (h1 = 0 ; h1 < h ; h1++) + { + SDL_memcpy4(buf,p,w); + for (w1 = 0 ; w1 < w ; w1++) + { + *(p+w1) = buf[w-(w1+1)]; + } + p += xres; + } +} + +void CTeletextDecoder::FlipVert(color_t *lfb, int xres,int x, int y, int w, int h) +{ + color_t buf[2048]; + color_t *p = lfb + x + y * xres, *p1, *p2; + int h1; + + for (h1 = 0 ; h1 < h/2 ; h1++) + { + p1 = (p+(h1*xres)); + p2 = (p+(h-(h1+1))*xres); + SDL_memcpy4(buf, p1, w); + SDL_memcpy4(p1, p2, w); + SDL_memcpy4(p2, buf, w); + } +} + +int CTeletextDecoder::ShapeCoord(int param, int curfontwidth, int curFontHeight) +{ + switch (param) + { + case S_W13: + return curfontwidth/3; + case S_W12: + return curfontwidth/2; + case S_W23: + return curfontwidth*2/3; + case S_W11: + return curfontwidth; + case S_WM3: + return curfontwidth-3; + case S_H13: + return curFontHeight/3; + case S_H12: + return curFontHeight/2; + case S_H23: + return curFontHeight*2/3; + case S_H11: + return curFontHeight; + default: + return param; + } +} + +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) +{ + if (!lfb || shapenumber < 0x20 || shapenumber > 0x7e || (shapenumber == 0x7e && clear)) + return; + + unsigned char *p = aShapes[shapenumber - 0x20]; + + if (*p == S_INV) + { + int t = fgcolor; + fgcolor = bgcolor; + bgcolor = t; + p++; + } + + if (clear) + FillRect(lfb, xres, x, y, curfontwidth, FontHeight, bgcolor); + + while (*p != S_END) + { + switch (*p++) + { + case S_FHL: + { + int offset = ShapeCoord(*p++, curfontwidth, curFontHeight); + DrawHLine(lfb, xres, x, y + offset, curfontwidth, fgcolor); + break; + } + case S_FVL: + { + int offset = ShapeCoord(*p++, curfontwidth, curFontHeight); + DrawVLine(lfb,xres,x + offset, y, FontHeight, fgcolor); + break; + } + case S_FLH: + FlipHorz(lfb,xres,x,y,curfontwidth, FontHeight); + break; + case S_FLV: + FlipVert(lfb,xres,x,y,curfontwidth, FontHeight); + break; + case S_BOX: + { + int xo = ShapeCoord(*p++, curfontwidth, curFontHeight); + int yo = ShapeCoord(*p++, curfontwidth, curFontHeight); + int w = ShapeCoord(*p++, curfontwidth, curFontHeight); + int h = ShapeCoord(*p++, curfontwidth, curFontHeight); + FillRect(lfb,xres,x + xo, y + yo, w, h, fgcolor); + break; + } + case S_TRA: + { + int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight); + int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight); + int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight); + int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight); + int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight); + int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight); + FillTrapez(lfb, xres,x + x0, y + y0, l0, x1-x0, y1-y0, l1, fgcolor); + break; + } + case S_BTR: + { + int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight); + int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight); + int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight); + int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight); + int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight); + int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight); + FillTrapez(lfb, xres, x + x0, y + y0, l0, x1-x0, y1-y0, l1, bgcolor); + break; + } + case S_LNK: + { + DrawShape(lfb,xres,x, y, ShapeCoord(*p, curfontwidth, curFontHeight), curfontwidth, FontHeight, curFontHeight, fgcolor, bgcolor, false); + break; + } + default: + break; + } + } +} + +void CTeletextDecoder::RenderCharIntern(TextRenderInfo_t* RenderInfo, int Char, TextPageAttr_t *Attribute, int zoom, int yoffset) +{ + int Row, Pitch; + int error, glyph; + color_t bgcolor, fgcolor; + int factor, xfactor; + unsigned char *sbitbuffer; + + int national_subset_local = m_txtCache->NationalSubset; + int curfontwidth = GetCurFontWidth(); + int t = curfontwidth; + m_RenderInfo.PosX += t; + int curfontwidth2 = GetCurFontWidth(); + m_RenderInfo.PosX -= t; + int alphachar = RenderChar(m_TextureBuffer+(yoffset)*m_RenderInfo.Width, m_RenderInfo.Width, Char, &m_RenderInfo.PosX, m_RenderInfo.PosY, Attribute, zoom, curfontwidth, curfontwidth2, m_RenderInfo.FontHeight, m_RenderInfo.TranspMode, m_RenderInfo.axdrcs, m_Ascender); + if (alphachar <= 0) return; + + if (zoom && Attribute->doubleh) + factor = 4; + else if (zoom || Attribute->doubleh) + factor = 2; + else + factor = 1; + + fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg); + if (m_RenderInfo.TranspMode && m_RenderInfo.PosY < 24*m_RenderInfo.FontHeight) + { + bgcolor = GetColorRGB(TXT_ColorTransp); + } + else + { + bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg); + } + + if (Attribute->doublew) + { + curfontwidth += curfontwidth2; + xfactor = 2; + } + else + xfactor = 1; + + if (!(glyph = FT_Get_Char_Index(m_Face, alphachar))) + { + CLog::Log(LOGERROR, "%s: <FT_Get_Char_Index for Char %x \"%c\" failed", __FUNCTION__, alphachar, alphachar); + + FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, factor*m_RenderInfo.FontHeight, bgcolor); + m_RenderInfo.PosX += curfontwidth; + return; + } + + if ((error = FTC_SBitCache_Lookup(m_Cache, &m_TypeTTF, glyph, &m_sBit, NULL)) != 0) + { + FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, m_RenderInfo.FontHeight, bgcolor); + m_RenderInfo.PosX += curfontwidth; + return; + } + + /* render char */ + sbitbuffer = m_sBit->buffer; + unsigned char localbuffer[1000]; // should be enough to store one character-bitmap... + // add diacritical marks + if (Attribute->diacrit) + { + FTC_SBit sbit_diacrit; + + if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) || (national_subset_local == NAT_UA)) + Char = G2table[1][0x20+ Attribute->diacrit]; + else if (national_subset_local == NAT_GR) + Char = G2table[2][0x20+ Attribute->diacrit]; + else if (national_subset_local == NAT_HB) + Char = G2table[3][0x20+ Attribute->diacrit]; + else if (national_subset_local == NAT_AR) + Char = G2table[4][0x20+ Attribute->diacrit]; + else + Char = G2table[0][0x20+ Attribute->diacrit]; + if ((glyph = FT_Get_Char_Index(m_Face, Char))) + { + if ((error = FTC_SBitCache_Lookup(m_Cache, &m_TypeTTF, glyph, &sbit_diacrit, NULL)) == 0) + { + sbitbuffer = localbuffer; + memcpy(sbitbuffer,m_sBit->buffer,m_sBit->pitch*m_sBit->height); + + for (Row = 0; Row < m_sBit->height; Row++) + { + for (Pitch = 0; Pitch < m_sBit->pitch; Pitch++) + { + if (sbit_diacrit->pitch > Pitch && sbit_diacrit->height > Row) + sbitbuffer[Row*m_sBit->pitch+Pitch] |= sbit_diacrit->buffer[Row*m_sBit->pitch+Pitch]; + } + } + } + } + } + + int backupTTFshiftY = m_RenderInfo.TTFShiftY; + if (national_subset_local == NAT_AR) + m_RenderInfo.TTFShiftY = backupTTFshiftY - 2; // for arabic TTF font should be shifted up slightly + + color_t *p; + int f; /* running counter for zoom factor */ + int he = m_sBit->height; // sbit->height should not be altered, I guess + Row = factor * (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY); + if (Row < 0) + { + sbitbuffer -= m_sBit->pitch*Row; + he += Row; + Row = 0; + } + else + { + FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, Row, bgcolor); /* fill upper margin */ + } + + if (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY + he > m_RenderInfo.FontHeight) + he = m_RenderInfo.FontHeight - m_Ascender + m_sBit->top - m_RenderInfo.TTFShiftY; /* limit char height to defined/calculated FontHeight */ + if (he < 0) he = m_RenderInfo.FontHeight; + + p = m_TextureBuffer + m_RenderInfo.PosX + (yoffset + m_RenderInfo.PosY + Row) * m_RenderInfo.Width; /* running pointer into framebuffer */ + for (Row = he; Row; Row--) /* row counts up, but down may be a little faster :) */ + { + int pixtodo = m_sBit->width; + color_t *pstart = p; + + for (int Bit = xfactor * (m_sBit->left + m_RenderInfo.TTFShiftX); Bit > 0; Bit--) /* fill left margin */ + { + for (f = factor-1; f >= 0; f--) + *(p + f*m_RenderInfo.Width) = bgcolor; + p++; + } + + for (Pitch = m_sBit->pitch; Pitch; Pitch--) + { + for (int Bit = 0x80; Bit; Bit >>= 1) + { + color_t color; + + if (--pixtodo < 0) + break; + + if (*sbitbuffer & Bit) /* bit set -> foreground */ + color = fgcolor; + else /* bit not set -> background */ + color = bgcolor; + + for (f = factor-1; f >= 0; f--) + *(p + f*m_RenderInfo.Width) = color; + p++; + + if (xfactor > 1) /* double width */ + { + for (f = factor-1; f >= 0; f--) + *(p + f*m_RenderInfo.Width) = color; + p++; + } + } + sbitbuffer++; + } + for (int Bit = (curfontwidth - xfactor*(m_sBit->width + m_sBit->left + m_RenderInfo.TTFShiftX)); + Bit > 0; Bit--) /* fill rest of char width */ + { + for (f = factor-1; f >= 0; f--) + *(p + f*m_RenderInfo.Width) = bgcolor; + p++; + } + + p = pstart + factor*m_RenderInfo.Width; + } + + Row = m_Ascender - m_sBit->top + he + m_RenderInfo.TTFShiftY; + FillRect(m_TextureBuffer, + m_RenderInfo.Width, + m_RenderInfo.PosX, + m_RenderInfo.PosY + yoffset + Row * factor, + curfontwidth, + (m_RenderInfo.FontHeight - Row) * factor, + bgcolor); /* fill lower margin */ + + if (Attribute->underline) + FillRect(m_TextureBuffer, + m_RenderInfo.Width, + m_RenderInfo.PosX, + m_RenderInfo.PosY + yoffset + (m_RenderInfo.FontHeight-2)* factor, + curfontwidth, + 2*factor, + fgcolor); /* underline char */ + + m_RenderInfo.PosX += curfontwidth; + m_RenderInfo.TTFShiftY = backupTTFshiftY; // restore TTFShiftY +} + +int CTeletextDecoder::RenderChar(color_t *buffer, // pointer to render buffer, min. FontHeight*2*xres + int xres, // length of 1 line in render buffer + int Char, // character to render + int *pPosX, // left border for rendering relative to *buffer, will be set to right border after rendering + int PosY, // vertical position of char in *buffer + TextPageAttr_t *Attribute,// Attributes of Char + bool zoom, // 1= character will be rendered in double height + int curfontwidth, // rendering width of character + int curfontwidth2, // rendering width of next character (needed for doublewidth) + int FontHeight, // height of character + bool transpmode, // 1= transparent display + unsigned char *axdrcs, // width and height of DRCS-chars + int Ascender) // Ascender of font +{ + int Row; + color_t bgcolor, fgcolor; + int factor, xfactor; + int national_subset_local = m_txtCache->NationalSubset; + int ymosaic[4]; + ymosaic[0] = 0; /* y-offsets for 2*3 mosaic */ + ymosaic[1] = (FontHeight + 1) / 3; + ymosaic[2] = (FontHeight * 2 + 1) / 3; + ymosaic[3] = FontHeight; + + if (Attribute->setX26) + { + national_subset_local = 0; // no national subset + } + + // G0+G2 set designation + if (Attribute->setG0G2 != 0x3f) + { + switch (Attribute->setG0G2) + { + case 0x20 : + national_subset_local = NAT_SC; + break; + case 0x24 : + national_subset_local = NAT_RB; + break; + case 0x25 : + national_subset_local = NAT_UA; + break; + case 0x37: + national_subset_local = NAT_GR; + break; + case 0x55: + national_subset_local = NAT_HB; + break; + case 0x47: + case 0x57: + national_subset_local = NAT_AR; + break; + default: + national_subset_local = CountryConversionTable[Attribute->setG0G2 & 0x07]; + break; + } + } + + if (Attribute->charset == C_G0S) // use secondary charset + national_subset_local = m_txtCache->NationalSubsetSecondary; + if (zoom && Attribute->doubleh) + factor = 4; + else if (zoom || Attribute->doubleh) + factor = 2; + else + factor = 1; + + if (Attribute->doublew) + { + curfontwidth += curfontwidth2; + xfactor = 2; + } + else + xfactor = 1; + + if (Char == 0xFF) /* skip doubleheight chars in lower line */ + { + *pPosX += curfontwidth; + return -1; + } + + /* get colors */ + if (Attribute->inverted) + { + int t = Attribute->fg; + Attribute->fg = Attribute->bg; + Attribute->bg = t; + } + fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg); + if (transpmode == true && PosY < 24*FontHeight) + { + bgcolor = GetColorRGB(TXT_ColorTransp); + } + else + { + bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg); + } + + /* handle mosaic */ + if ((Attribute->charset == C_G1C || Attribute->charset == C_G1S) && + ((Char&0xA0) == 0x20)) + { + int w1 = (curfontwidth / 2 ) *xfactor; + int w2 = (curfontwidth - w1) *xfactor; + + Char = (Char & 0x1f) | ((Char & 0x40) >> 1); + if (Attribute->charset == C_G1S) /* separated mosaic */ + { + for (int y = 0; y < 3; y++) + { + FillRectMosaicSeparated(buffer, xres,*pPosX, PosY + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x01); + FillRectMosaicSeparated(buffer, xres,*pPosX + w1, PosY + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x02); + Char >>= 2; + } + } + else + { + for (int y = 0; y < 3; y++) + { + FillRect(buffer, xres, *pPosX, PosY + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x01) ? fgcolor : bgcolor); + FillRect(buffer, xres, *pPosX + w1, PosY + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x02) ? fgcolor : bgcolor); + Char >>= 2; + } + } + + *pPosX += curfontwidth; + return 0;; + } + + if (Attribute->charset == C_G3) + { + if (Char < 0x20 || Char > 0x7d) + { + Char = 0x20; + } + else + { + if (*aShapes[Char - 0x20] == S_CHR) + { + unsigned char *p = aShapes[Char - 0x20]; + Char = (*(p+1) <<8) + (*(p+2)); + } + else if (*aShapes[Char - 0x20] == S_ADT) + { + if (buffer) + { + int x,y,f,c; + color_t* p = buffer + *pPosX + PosY* xres; + for (y=0; y<FontHeight;y++) + { + for (f=0; f<factor; f++) + { + for (x=0; x<curfontwidth*xfactor;x++) + { + c = (y&4 ? (x/3)&1 :((x+3)/3)&1); + *(p+x) = (c ? fgcolor : bgcolor); + } + p += xres; + } + } + } + *pPosX += curfontwidth; + return 0; + } + else + { + DrawShape(buffer, xres,*pPosX, PosY, Char, curfontwidth, FontHeight, factor*FontHeight, fgcolor, bgcolor, true); + *pPosX += curfontwidth; + return 0; + } + } + } + else if (Attribute->charset >= C_OFFSET_DRCS) + { + TextCachedPage_t *pcache = m_txtCache->astCachetable[(Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs][Attribute->charset & 0x0f]; + if (pcache) + { + unsigned char drcs_data[23*40]; + g_application.m_pPlayer->LoadPage((Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs, Attribute->charset & 0x0f, drcs_data); + unsigned char *p; + if (Char < 23*2) + p = drcs_data + 20*Char; + else if (pcache->pageinfo.p24) + p = pcache->pageinfo.p24 + 20*(Char - 23*2); + else + { + FillRect(buffer, xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor); + *pPosX += curfontwidth; + return 0; + } + axdrcs[12] = curfontwidth; /* adjust last x-offset according to position, FIXME: double width */ + RenderDRCS(xres, p, buffer + *pPosX + PosY * xres, axdrcs, fgcolor, bgcolor); + } + else + { + FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor); + } + *pPosX += curfontwidth; + return 0; + } + else if (Attribute->charset == C_G2 && Char >= 0x20 && Char <= 0x7F) + { + if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) || (national_subset_local == NAT_UA)) + Char = G2table[1][Char-0x20]; + else if (national_subset_local == NAT_GR) + Char = G2table[2][Char-0x20]; + else if (national_subset_local == NAT_AR) + Char = G2table[3][Char-0x20]; + else + Char = G2table[0][Char-0x20]; + + //if (Char == 0x7F) + //{ + // FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*Ascender, fgcolor); + // FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor); + // *pPosX += curfontwidth; + // return 0; + //} + } + else if (national_subset_local == NAT_SC && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for serbian/croatian */ + Char = G0table[0][Char-0x20]; + else if (national_subset_local == NAT_RB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for russian/bulgarian */ + Char = G0table[1][Char-0x20]; + else if (national_subset_local == NAT_UA && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for ukrainian */ + Char = G0table[2][Char-0x20]; + else if (national_subset_local == NAT_GR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for greek */ + Char = G0table[3][Char-0x20]; + else if (national_subset_local == NAT_HB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for hebrew */ + Char = G0table[4][Char-0x20]; + else if (national_subset_local == NAT_AR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for arabic */ + Char = G0table[5][Char-0x20]; + else + { + /* load char */ + switch (Char) + { + case 0x00: + case 0x20: + FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor); + *pPosX += curfontwidth; + return -3; + case 0x23: + case 0x24: + Char = nationaltable23[national_subset_local][Char-0x23]; + break; + case 0x40: + Char = nationaltable40[national_subset_local]; + break; + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + Char = nationaltable5b[national_subset_local][Char-0x5B]; + break; + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + Char = nationaltable7b[national_subset_local][Char-0x7B]; + break; + case 0x7F: + FillRect(buffer,xres,*pPosX, PosY , curfontwidth, factor*Ascender, fgcolor); + FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor); + *pPosX += curfontwidth; + return 0; + case 0xE0: /* |- */ + DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor); + DrawVLine(buffer,xres,*pPosX, PosY +1, FontHeight -1, fgcolor); + FillRect(buffer,xres,*pPosX +1, PosY +1, curfontwidth-1, FontHeight-1, bgcolor); + *pPosX += curfontwidth; + return 0; + case 0xE1: /* - */ + DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor); + FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight-1, bgcolor); + *pPosX += curfontwidth; + return 0; + case 0xE2: /* -| */ + DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor); + DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY +1, FontHeight -1, fgcolor); + FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth-1, FontHeight-1, bgcolor); + *pPosX += curfontwidth; + return 0; + case 0xE3: /* | */ + DrawVLine(buffer,xres,*pPosX, PosY, FontHeight, fgcolor); + FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor); + *pPosX += curfontwidth; + return 0; + case 0xE4: /* | */ + DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight, fgcolor); + FillRect(buffer,xres,*pPosX, PosY, curfontwidth -1, FontHeight, bgcolor); + *pPosX += curfontwidth; + return 0; + case 0xE5: /* |_ */ + DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor); + DrawVLine(buffer,xres,*pPosX, PosY, FontHeight -1, fgcolor); + FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth-1, FontHeight-1, bgcolor); + *pPosX += curfontwidth; + return 0; + case 0xE6: /* _ */ + DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor); + FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight-1, bgcolor); + *pPosX += curfontwidth; + return 0; + case 0xE7: /* _| */ + DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor); + DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight -1, fgcolor); + FillRect(buffer,xres,*pPosX, PosY, curfontwidth-1, FontHeight-1, bgcolor); + *pPosX += curfontwidth; + return 0; + case 0xE8: /* Ii */ + FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor); + for (Row=0; Row < curfontwidth/2; Row++) + DrawVLine(buffer,xres,*pPosX + Row, PosY + Row, FontHeight - Row, fgcolor); + *pPosX += curfontwidth; + return 0; + case 0xE9: /* II */ + FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, FontHeight, fgcolor); + FillRect(buffer,xres,*pPosX + curfontwidth/2, PosY, (curfontwidth+1)/2, FontHeight, bgcolor); + *pPosX += curfontwidth; + return 0; + case 0xEA: /* ° */ + FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight, bgcolor); + FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, curfontwidth/2, fgcolor); + *pPosX += curfontwidth; + return 0; + case 0xEB: /* ¬ */ + FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight -1, bgcolor); + for (Row=0; Row < curfontwidth/2; Row++) + DrawHLine(buffer,xres,*pPosX + Row, PosY + Row, curfontwidth - Row, fgcolor); + *pPosX += curfontwidth; + return 0; + case 0xEC: /* -- */ + FillRect(buffer, xres,*pPosX, PosY, curfontwidth, curfontwidth/2, fgcolor); + FillRect(buffer, xres,*pPosX, PosY + curfontwidth/2, curfontwidth, FontHeight - curfontwidth/2, bgcolor); + *pPosX += curfontwidth; + return 0; + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + Char = arrowtable[Char - 0xED]; + break; + default: + break; + } + } + if (Char <= 0x20) + { + FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor); + *pPosX += curfontwidth; + return -2; + } + return Char; // Char is an alphanumeric unicode character +} + +TextPageinfo_t* CTeletextDecoder::DecodePage(bool showl25, // 1=decode Level2.5-graphics + unsigned char* PageChar, // page buffer, min. 25*40 + TextPageAttr_t *PageAtrb, // attribut buffer, min 25*40 + bool HintMode, // 1=show hidden information + bool showflof) // 1=decode FLOF-line +{ + int col; + int hold, dhset; + int foreground, background, doubleheight, doublewidth, charset, previous_charset, mosaictype, IgnoreAtBlackBgSubst, concealed, flashmode, boxwin; + unsigned char held_mosaic, *p; + TextCachedPage_t *pCachedPage; + + /* copy page to decode buffer */ + if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff) /* not cached: do nothing */ + return NULL; + + if (m_txtCache->ZapSubpageManual) + pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage]; + else + pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPageTable[m_txtCache->Page]]; + if (!pCachedPage) /* not cached: do nothing */ + return NULL; + + g_application.m_pPlayer->LoadPage(m_txtCache->Page, m_txtCache->SubPage, &PageChar[40]); + + memcpy(&PageChar[8], pCachedPage->p0, 24); /* header line without TimeString */ + + TextPageinfo_t* PageInfo = &(pCachedPage->pageinfo); + if (PageInfo->p24) + memcpy(&PageChar[24*40], PageInfo->p24, 40); /* line 25 for FLOF */ + + /* copy TimeString */ + memcpy(&PageChar[32], &m_txtCache->TimeString, 8); + + bool boxed; + /* check for newsflash & subtitle */ + if (PageInfo->boxed && IsDec(m_txtCache->Page)) + boxed = true; + else + boxed = false; + + + /* modify header */ + if (boxed) + { + memset(PageChar, ' ', 40); + } + else + { + memset(PageChar, ' ', 8); + CDVDTeletextTools::Hex2Str((char*)PageChar+3, m_txtCache->Page); + if (m_txtCache->SubPage) + { + *(PageChar+4) ='/'; + *(PageChar+5) ='0'; + CDVDTeletextTools::Hex2Str((char*)PageChar+6, m_txtCache->SubPage); + } + } + + if (!IsDec(m_txtCache->Page)) + { + TextPageAttr_t atr = { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}; + if (PageInfo->function == FUNC_MOT) /* magazine organization table */ + { + for (col = 0; col < 24*40; col++) + PageAtrb[col] = atr; + for (col = 40; col < 24*40; col++) + PageChar[col] = number2char(PageChar[col]); + boxed = false; + return PageInfo; /* don't interpret irregular pages */ + } + else if (PageInfo->function == FUNC_GPOP || PageInfo->function == FUNC_POP) /* object definitions */ + { + for (int col = 0; col < 24*40; col++) + PageAtrb[col] = atr; + + p = PageChar + 40; + for (int row = 1; row < 12; row++) + { + *p++ = number2char(row); /* first column: number (0-9, A-..) */ + for (int col = 1; col < 40; col += 3) + { + int d = CDVDTeletextTools::deh24(p); + if (d < 0) + { + memcpy(p, "???", 3); + p += 3; + } + else + { + *p++ = number2char((d >> 6) & 0x1f); /* mode */ + *p++ = number2char(d & 0x3f); /* address */ + *p++ = number2char((d >> 11) & 0x7f); /* data */ + } + } + } + boxed = false; + return PageInfo; /* don't interpret irregular pages */ + } + else if (PageInfo->function == FUNC_GDRCS || PageInfo->function == FUNC_DRCS) /* character definitions */ + { + boxed = false; + return PageInfo; /* don't interpret irregular pages */ + } + else + { + int h, parityerror = 0; + + for (int i = 0; i < 8; i++) + PageAtrb[i] = atr; + + /* decode parity/hamming */ + for (int i = 40; i < sizeof(PageChar); i++) + { + PageAtrb[i] = atr; + p = PageChar + i; + h = dehamming[*p]; + if (parityerror && h != 0xFF) /* if no regular page (after any parity error) */ + CDVDTeletextTools::Hex2Str((char*)p, h); /* first try dehamming */ + else + { + if (*p == ' ' || deparity[*p] != ' ') /* correct parity */ + *p &= 127; + else + { + parityerror = 1; + if (h != 0xFF) /* first parity error: try dehamming */ + CDVDTeletextTools::Hex2Str((char*)p, h); + else + *p = ' '; + } + } + } + if (parityerror) + { + boxed = false; + return PageInfo; /* don't interpret irregular pages */ + } + } + } + int mosaic_pending,esc_pending; + /* decode */ + for (int row = 0; row < ((showflof && PageInfo->p24) ? 25 : 24); row++) + { + /* start-of-row default conditions */ + foreground = TXT_ColorWhite; + background = TXT_ColorBlack; + doubleheight = 0; + doublewidth = 0; + charset = previous_charset = C_G0P; // remember charset for switching back after mosaic charset was used + mosaictype = 0; + concealed = 0; + flashmode = 0; + hold = 0; + boxwin = 0; + held_mosaic = ' '; + dhset = 0; + IgnoreAtBlackBgSubst = 0; + mosaic_pending = esc_pending = 0; // we need to render at least one mosaic char if 'esc' is received immediatly after mosac charset switch on + + if (boxed && memchr(&PageChar[row*40], start_box, 40) == 0) + { + foreground = TXT_ColorTransp; + background = TXT_ColorTransp; + } + + for (int col = 0; col < 40; col++) + { + int index = row*40 + col; + + PageAtrb[index].fg = foreground; + PageAtrb[index].bg = background; + PageAtrb[index].charset = charset; + PageAtrb[index].doubleh = doubleheight; + PageAtrb[index].doublew = (col < 39 ? doublewidth : 0); + PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst; + PageAtrb[index].concealed = concealed; + PageAtrb[index].flashing = flashmode; + PageAtrb[index].boxwin = boxwin; + PageAtrb[index].inverted = 0; // only relevant for Level 2.5 + PageAtrb[index].underline = 0; // only relevant for Level 2.5 + PageAtrb[index].diacrit = 0; // only relevant for Level 2.5 + PageAtrb[index].setX26 = 0; // only relevant for Level 2.5 + PageAtrb[index].setG0G2 = 0x3f; // only relevant for Level 2.5 + + if (PageChar[index] < ' ') + { + if (esc_pending) { // mosaic char has been rendered and we can switch charsets + charset = previous_charset; + if (charset == C_G0P) + charset = previous_charset = C_G0S; + else if (charset == C_G0S) + charset = previous_charset = C_G0P; + esc_pending = 0; + } + switch (PageChar[index]) + { + case alpha_black: + case alpha_red: + case alpha_green: + case alpha_yellow: + case alpha_blue: + case alpha_magenta: + case alpha_cyan: + case alpha_white: + concealed = 0; + foreground = PageChar[index] - alpha_black + TXT_ColorBlack; + if (col == 0 && PageChar[index] == alpha_white) + PageAtrb[index].fg = TXT_ColorBlack; // indicate level 1 color change on column 0; (hack) + if ((charset!=C_G0P) && (charset!=C_G0S)) // we need to change charset to state it was before mosaic + charset = previous_charset; + break; + + case flash: + flashmode = 1; + break; + + case steady: + flashmode = 0; + PageAtrb[index].flashing = 0; + break; + + case end_box: + boxwin = 0; + IgnoreAtBlackBgSubst = 0; + break; + + case start_box: + if (!boxwin) + boxwin = 1; + break; + + case normal_size: + doubleheight = 0; + doublewidth = 0; + PageAtrb[index].doubleh = doubleheight; + PageAtrb[index].doublew = doublewidth; + break; + + case double_height: + if (row < 23) + { + doubleheight = 1; + dhset = 1; + } + doublewidth = 0; + + break; + + case double_width: + if (col < 39) + doublewidth = 1; + doubleheight = 0; + break; + + case double_size: + if (row < 23) + { + doubleheight = 1; + dhset = 1; + } + if (col < 39) + doublewidth = 1; + break; + + case mosaic_black: + case mosaic_red: + case mosaic_green: + case mosaic_yellow: + case mosaic_blue: + case mosaic_magenta: + case mosaic_cyan: + case mosaic_white: + concealed = 0; + foreground = PageChar[index] - mosaic_black + TXT_ColorBlack; + if ((charset==C_G0P) || (charset==C_G0S)) + previous_charset=charset; + charset = mosaictype ? C_G1S : C_G1C; + mosaic_pending = 1; + break; + + case conceal: + PageAtrb[index].concealed = 1; + concealed = 1; + if (!HintMode) + { + foreground = background; + PageAtrb[index].fg = foreground; + } + break; + + case contiguous_mosaic: + mosaictype = 0; + if (charset == C_G1S) + { + charset = C_G1C; + PageAtrb[index].charset = charset; + } + break; + + case separated_mosaic: + mosaictype = 1; + if (charset == C_G1C) + { + charset = C_G1S; + PageAtrb[index].charset = charset; + } + break; + + case esc: + if (!mosaic_pending) { // if mosaic is pending we need to wait before mosaic arrives + if ((charset != C_G0P) && (charset != C_G0S)) // we need to switch to charset which was active before mosaic + charset = previous_charset; + if (charset == C_G0P) + charset = previous_charset = C_G0S; + else if (charset == C_G0S) + charset = previous_charset = C_G0P; + } else esc_pending = 1; + break; + + case black_background: + background = TXT_ColorBlack; + IgnoreAtBlackBgSubst = 0; + PageAtrb[index].bg = background; + PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst; + break; + + case new_background: + background = foreground; + if (background == TXT_ColorBlack) + IgnoreAtBlackBgSubst = 1; + else + IgnoreAtBlackBgSubst = 0; + PageAtrb[index].bg = background; + PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst; + break; + + case hold_mosaic: + hold = 1; + break; + + case release_mosaic: + hold = 2; + break; + } + + /* handle spacing attributes */ + if (hold && (PageAtrb[index].charset == C_G1C || PageAtrb[index].charset == C_G1S)) + PageChar[index] = held_mosaic; + else + PageChar[index] = ' '; + + if (hold == 2) + hold = 0; + } + else /* char >= ' ' */ + { + mosaic_pending = 0; // charset will be switched next if esc_pending + /* set new held-mosaic char */ + if ((charset == C_G1C || charset == C_G1S) && + ((PageChar[index]&0xA0) == 0x20)) + held_mosaic = PageChar[index]; + if (PageAtrb[index].doubleh) + PageChar[index + 40] = 0xFF; + + } + if (!(charset == C_G1C || charset == C_G1S)) + held_mosaic = ' '; /* forget if outside mosaic */ + + } /* for col */ + + /* skip row if doubleheight */ + if (row < 23 && dhset) + { + for (int col = 0; col < 40; col++) + { + int index = row*40 + col; + PageAtrb[index+40].bg = PageAtrb[index].bg; + PageAtrb[index+40].fg = TXT_ColorWhite; + if (!PageAtrb[index].doubleh) + PageChar[index+40] = ' '; + PageAtrb[index+40].flashing = 0; + PageAtrb[index+40].charset = C_G0P; + PageAtrb[index+40].doubleh = 0; + PageAtrb[index+40].doublew = 0; + PageAtrb[index+40].IgnoreAtBlackBgSubst = 0; + PageAtrb[index+40].concealed = 0; + PageAtrb[index+40].flashing = 0; + PageAtrb[index+40].boxwin = PageAtrb[index].boxwin; + } + row++; + } + } /* for row */ + m_txtCache->FullScrColor = TXT_ColorBlack; + + if (showl25) + Eval_l25(PageChar, PageAtrb, HintMode); + + /* handle Black Background Color Substitution and transparency (CLUT1#0) */ + { + int o = 0; + char bitmask ; + + for (int r = 0; r < 25; r++) + { + for (int c = 0; c < 40; c++) + { + bitmask = (PageAtrb[o].bg == 0x08 ? 0x08 : 0x00) | (m_txtCache->FullRowColor[r] == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed; + switch (bitmask) + { + case 0x08: + case 0x0b: + if (m_txtCache->FullRowColor[r] == 0x08) + PageAtrb[o].bg = m_txtCache->FullScrColor; + else + PageAtrb[o].bg = m_txtCache->FullRowColor[r]; + break; + case 0x01: + case 0x05: + case 0x09: + case 0x0a: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + PageAtrb[o].bg = TXT_ColorTransp; + break; + } + bitmask = (PageAtrb[o].fg == 0x08 ? 0x08 : 0x00) | (m_txtCache->FullRowColor[r] == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed; + switch (bitmask) + { + case 0x08: + case 0x0b: + if (m_txtCache->FullRowColor[r] == 0x08) + PageAtrb[o].fg = m_txtCache->FullScrColor; + else + PageAtrb[o].fg = m_txtCache->FullRowColor[r]; + break; + case 0x01: + case 0x05: + case 0x09: + case 0x0a: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + PageAtrb[o].fg = TXT_ColorTransp; + break; + } + o++; + } + } + } + return PageInfo; +} + +void CTeletextDecoder::Eval_l25(unsigned char* PageChar, TextPageAttr_t *PageAtrb, bool HintMode) +{ + memset(m_txtCache->FullRowColor, 0, sizeof(m_txtCache->FullRowColor)); + m_txtCache->FullScrColor = TXT_ColorBlack; + m_txtCache->ColorTable = NULL; + + if (!m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage]) + return; + + /* normal page */ + if (IsDec(m_txtCache->Page)) + { + unsigned char APx0, APy0, APx, APy; + TextPageinfo_t *pi = &(m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage]->pageinfo); + TextCachedPage_t *pmot = m_txtCache->astCachetable[(m_txtCache->Page & 0xf00) | 0xfe][0]; + int p26Received = 0; + int BlackBgSubst = 0; + int ColorTableRemapping = 0; + + m_txtCache->pop = m_txtCache->gpop = m_txtCache->drcs = m_txtCache->gdrcs = 0; + + if (pi->ext) + { + TextExtData_t *e = pi->ext; + + if (e->p26[0]) + p26Received = 1; + + if (e->p27) + { + Textp27_t *p27 = e->p27; + if (p27[0].l25) + m_txtCache->gpop = p27[0].page; + if (p27[1].l25) + m_txtCache->pop = p27[1].page; + if (p27[2].l25) + m_txtCache->gdrcs = p27[2].page; + if (p27[3].l25) + m_txtCache->drcs = p27[3].page; + } + + if (e->p28Received) + { + m_txtCache->ColorTable = e->bgr; + BlackBgSubst = e->BlackBgSubst; + ColorTableRemapping = e->ColorTableRemapping; + memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor)); + m_txtCache->FullScrColor = e->DefScreenColor; + m_txtCache->NationalSubset = SetNational(e->DefaultCharset); + m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset); + } /* e->p28Received */ + } + + if (!m_txtCache->ColorTable && m_txtCache->astP29[m_txtCache->Page >> 8]) + { + TextExtData_t *e = m_txtCache->astP29[m_txtCache->Page >> 8]; + m_txtCache->ColorTable = e->bgr; + BlackBgSubst = e->BlackBgSubst; + ColorTableRemapping = e->ColorTableRemapping; + memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor)); + m_txtCache->FullScrColor = e->DefScreenColor; + m_txtCache->NationalSubset = SetNational(e->DefaultCharset); + m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset); + } + + if (ColorTableRemapping) + { + for (int i = 0; i < 25*40; i++) + { + PageAtrb[i].fg += MapTblFG[ColorTableRemapping - 1]; + if (!BlackBgSubst || PageAtrb[i].bg != TXT_ColorBlack || PageAtrb[i].IgnoreAtBlackBgSubst) + PageAtrb[i].bg += MapTblBG[ColorTableRemapping - 1]; + } + } + + /* determine ?pop/?drcs from MOT */ + if (pmot) + { + unsigned char pmot_data[23*40]; + g_application.m_pPlayer->LoadPage((m_txtCache->Page & 0xf00) | 0xfe, 0, pmot_data); + + unsigned char *p = pmot_data; /* start of link data */ + int o = 2 * (((m_txtCache->Page & 0xf0) >> 4) * 10 + (m_txtCache->Page & 0x0f)); /* offset of links for current page */ + int opop = p[o] & 0x07; /* index of POP link */ + int odrcs = p[o+1] & 0x07; /* index of DRCS link */ + unsigned char obj[3*4*4]; /* types* objects * (triplet,packet,subp,high) */ + unsigned char type,ct, tstart = 4*4; + memset(obj,0,sizeof(obj)); + + if (p[o] & 0x08) /* GPOP data used */ + { + if (!m_txtCache->gpop || !(p[18*40] & 0x08)) /* no p27 data or higher prio of MOT link */ + { + m_txtCache->gpop = ((p[18*40] << 8) | (p[18*40+1] << 4) | p[18*40+2]) & 0x7ff; + if ((m_txtCache->gpop & 0xff) == 0xff) + m_txtCache->gpop = 0; + else + { + if (m_txtCache->gpop < 0x100) + m_txtCache->gpop += 0x800; + if (!p26Received) + { + ct = 2; + while (ct) + { + ct--; + type = (p[18*40+5] >> 2*ct) & 0x03; + + if (type == 0) continue; + obj[(type-1)*(tstart)+ct*4 ] = 3 * ((p[18*40+7+ct*2] >> 1) & 0x03) + type; //triplet + obj[(type-1)*(tstart)+ct*4+1] = ((p[18*40+7+ct*2] & 0x08) >> 3) + 1 ; //packet + obj[(type-1)*(tstart)+ct*4+2] = p[18*40+6+ct*2] & 0x0f ; //subp + obj[(type-1)*(tstart)+ct*4+3] = p[18*40+7+ct*2] & 0x01 ; //high + } + } + } + } + } + if (opop) /* POP data used */ + { + opop = 18*40 + 10*opop; /* offset to POP link */ + if (!m_txtCache->pop || !(p[opop] & 0x08)) /* no p27 data or higher prio of MOT link */ + { + m_txtCache->pop = ((p[opop] << 8) | (p[opop+1] << 4) | p[opop+2]) & 0x7ff; + if ((m_txtCache->pop & 0xff) == 0xff) + m_txtCache->pop = 0; + else + { + if (m_txtCache->pop < 0x100) + m_txtCache->pop += 0x800; + if (!p26Received) + { + ct = 2; + while (ct) + { + ct--; + type = (p[opop+5] >> 2*ct) & 0x03; + + if (type == 0) continue; + obj[(type-1)*(tstart)+(ct+2)*4 ] = 3 * ((p[opop+7+ct*2] >> 1) & 0x03) + type; //triplet + obj[(type-1)*(tstart)+(ct+2)*4+1] = ((p[opop+7+ct*2] & 0x08) >> 3) + 1 ; //packet + obj[(type-1)*(tstart)+(ct+2)*4+2] = p[opop+6+ct*2] ; //subp + obj[(type-1)*(tstart)+(ct+2)*4+3] = p[opop+7+ct*2] & 0x01 ; //high + } + } + } + } + } + // eval default objects in correct order + for (int i = 0; i < 12; i++) + { + if (obj[i*4] != 0) + { + APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0; + 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); + } + } + + if (p[o+1] & 0x08) /* GDRCS data used */ + { + if (!m_txtCache->gdrcs || !(p[20*40] & 0x08)) /* no p27 data or higher prio of MOT link */ + { + m_txtCache->gdrcs = ((p[20*40] << 8) | (p[20*40+1] << 4) | p[20*40+2]) & 0x7ff; + if ((m_txtCache->gdrcs & 0xff) == 0xff) + m_txtCache->gdrcs = 0; + else if (m_txtCache->gdrcs < 0x100) + m_txtCache->gdrcs += 0x800; + } + } + if (odrcs) /* DRCS data used */ + { + odrcs = 20*40 + 4*odrcs; /* offset to DRCS link */ + if (!m_txtCache->drcs || !(p[odrcs] & 0x08)) /* no p27 data or higher prio of MOT link */ + { + m_txtCache->drcs = ((p[odrcs] << 8) | (p[odrcs+1] << 4) | p[odrcs+2]) & 0x7ff; + if ((m_txtCache->drcs & 0xff) == 0xff) + m_txtCache->drcs = 0; + else if (m_txtCache->drcs < 0x100) + m_txtCache->drcs += 0x800; + } + } + if (m_txtCache->astCachetable[m_txtCache->gpop][0]) + m_txtCache->astCachetable[m_txtCache->gpop][0]->pageinfo.function = FUNC_GPOP; + if (m_txtCache->astCachetable[m_txtCache->pop][0]) + m_txtCache->astCachetable[m_txtCache->pop][0]->pageinfo.function = FUNC_POP; + if (m_txtCache->astCachetable[m_txtCache->gdrcs][0]) + m_txtCache->astCachetable[m_txtCache->gdrcs][0]->pageinfo.function = FUNC_GDRCS; + if (m_txtCache->astCachetable[m_txtCache->drcs][0]) + m_txtCache->astCachetable[m_txtCache->drcs][0]->pageinfo.function = FUNC_DRCS; + } /* if mot */ + + /* evaluate local extension data from p26 */ + if (p26Received) + { + APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0; + 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 */ + } + + { + int o = 0; + for (int r = 0; r < 25; r++) + { + for (int c = 0; c < 40; c++) + { + if (BlackBgSubst && PageAtrb[o].bg == TXT_ColorBlack && !(PageAtrb[o].IgnoreAtBlackBgSubst)) + { + if (m_txtCache->FullRowColor[r] == 0x08) + PageAtrb[o].bg = m_txtCache->FullScrColor; + else + PageAtrb[o].bg = m_txtCache->FullRowColor[r]; + } + o++; + } + } + } + + if (!HintMode) + { + for (int i = 0; i < 25*40; i++) + { + if (PageAtrb[i].concealed) PageAtrb[i].fg = PageAtrb[i].bg; + } + } + } /* is_dec(page) */ +} + +/* dump interpreted object data to stdout */ +/* in: 18 bit object data */ +/* out: termination info, >0 if end of object */ +void CTeletextDecoder::Eval_Object(int iONr, TextCachedPage_t *pstCachedPage, + unsigned char *pAPx, unsigned char *pAPy, + unsigned char *pAPx0, unsigned char *pAPy0, + tObjType ObjType, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb) +{ + int iOData; + int iONr1 = iONr + 1; /* don't terminate after first triplet */ + unsigned char drcssubp=0, gdrcssubp=0; + signed char endcol = -1; /* last column to which to extend attribute changes */ + 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 */ + + do + { + iOData = iTripletNumber2Data(iONr, pstCachedPage, pagedata); /* get triplet data, next triplet */ + if (iOData < 0) /* invalid number, not cached, or hamming error: terminate */ + break; + + if (endcol < 0) + { + if (ObjType == OBJ_ACTIVE) + { + endcol = 40; + } + else if (ObjType == OBJ_ADAPTIVE) /* search end of line */ + { + for (int i = iONr; i <= 506; i++) + { + int iTempOData = iTripletNumber2Data(i, pstCachedPage, pagedata); /* get triplet data, next triplet */ + int iAddress = (iTempOData ) & 0x3f; + int iMode = (iTempOData >> 6) & 0x1f; + //int iData = (iTempOData >> 11) & 0x7f; + if (iTempOData < 0 || /* invalid number, not cached, or hamming error: terminate */ + (iAddress >= 40 /* new row: row address and */ + && (iMode == 0x01 || /* Full Row Color or */ + iMode == 0x04 || /* Set Active Position */ + (iMode >= 0x15 && iMode <= 0x17) || /* Object Definition */ + iMode == 0x17))) /* Object Termination */ + break; + if (iAddress < 40 && iMode != 0x06) + endcol = iAddress; + } + } + } + iONr++; + } + while (0 == Eval_Triplet(iOData, pstCachedPage, pAPx, pAPy, pAPx0, pAPy0, &drcssubp, &gdrcssubp, &endcol, &attrPassive, pagedata, PageChar, PageAtrb) || iONr1 == iONr); /* repeat until termination reached */ +} + +void CTeletextDecoder::Eval_NumberedObject(int p, int s, int packet, int triplet, int high, + unsigned char *pAPx, unsigned char *pAPy, + unsigned char *pAPx0, unsigned char *pAPy0, unsigned char* PageChar, TextPageAttr_t* PageAtrb) +{ + if (!packet || 0 == m_txtCache->astCachetable[p][s]) + return; + + unsigned char pagedata[23*40]; + g_application.m_pPlayer->LoadPage(p, s,pagedata); + + int idata = CDVDTeletextTools::deh24(pagedata + 40*(packet-1) + 1 + 3*triplet); + int iONr; + + if (idata < 0) /* hamming error: ignore triplet */ + return; + if (high) + iONr = idata >> 9; /* triplet number of odd object data */ + else + iONr = idata & 0x1ff; /* triplet number of even object data */ + if (iONr <= 506) + { + Eval_Object(iONr, m_txtCache->astCachetable[p][s], pAPx, pAPy, pAPx0, pAPy0, (tObjType)(triplet % 3),pagedata, PageChar, PageAtrb); + } +} + +int CTeletextDecoder::Eval_Triplet(int iOData, TextCachedPage_t *pstCachedPage, + unsigned char *pAPx, unsigned char *pAPy, + unsigned char *pAPx0, unsigned char *pAPy0, + unsigned char *drcssubp, unsigned char *gdrcssubp, + signed char *endcol, TextPageAttr_t *attrPassive, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb) +{ + int iAddress = (iOData ) & 0x3f; + int iMode = (iOData >> 6) & 0x1f; + int iData = (iOData >> 11) & 0x7f; + + if (iAddress < 40) /* column addresses */ + { + int offset; /* offset to PageChar and PageAtrb */ + + if (iMode != 0x06) + *pAPx = iAddress; /* new Active Column */ + offset = (*pAPy0 + *pAPy) * 40 + *pAPx0 + *pAPx; /* offset to PageChar and PageAtrb */ + + switch (iMode) + { + case 0x00: + if (0 == (iData>>5)) + { + int newcolor = iData & 0x1f; + if (*endcol < 0) /* passive object */ + attrPassive->fg = newcolor; + else if (*endcol == 40) /* active object */ + { + TextPageAttr_t *p = &PageAtrb[offset]; + int oldcolor = (p)->fg; /* current color (set-after) */ + int c = *pAPx0 + *pAPx; /* current column absolute */ + do + { + p->fg = newcolor; + p++; + c++; + } while (c < 40 && p->fg == oldcolor); /* stop at change by level 1 page */ + } + else /* adaptive object */ + { + TextPageAttr_t *p = &PageAtrb[offset]; + int c = *pAPx; /* current column relative to object origin */ + do + { + p->fg = newcolor; + p++; + c++; + } while (c <= *endcol); + } + } + break; + case 0x01: + if (iData >= 0x20) + { + PageChar[offset] = iData; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_G1C; /* FIXME: separated? */ + PageAtrb[offset] = *attrPassive; + } + else if (PageAtrb[offset].charset != C_G1S) + PageAtrb[offset].charset = C_G1C; /* FIXME: separated? */ + } + break; + case 0x02: + case 0x0b: + PageChar[offset] = iData; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_G3; + PageAtrb[offset] = *attrPassive; + } + else + PageAtrb[offset].charset = C_G3; + break; + case 0x03: + if (0 == (iData>>5)) + { + int newcolor = iData & 0x1f; + if (*endcol < 0) /* passive object */ + attrPassive->bg = newcolor; + else if (*endcol == 40) /* active object */ + { + TextPageAttr_t *p = &PageAtrb[offset]; + int oldcolor = (p)->bg; /* current color (set-after) */ + int c = *pAPx0 + *pAPx; /* current column absolute */ + do + { + p->bg = newcolor; + if (newcolor == TXT_ColorBlack) + p->IgnoreAtBlackBgSubst = 1; + p++; + c++; + } while (c < 40 && p->bg == oldcolor); /* stop at change by level 1 page */ + } + else /* adaptive object */ + { + TextPageAttr_t *p = &PageAtrb[offset]; + int c = *pAPx; /* current column relative to object origin */ + do + { + p->bg = newcolor; + if (newcolor == TXT_ColorBlack) + p->IgnoreAtBlackBgSubst = 1; + p++; + c++; + } while (c <= *endcol); + } + } + break; + case 0x06: + /* ignore */ + break; + case 0x07: + if ((iData & 0x60) != 0) break; // reserved data field + if (*endcol < 0) /* passive object */ + { + attrPassive->flashing=iData & 0x1f; + PageAtrb[offset] = *attrPassive; + } + else + PageAtrb[offset].flashing=iData & 0x1f; + break; + case 0x08: + if (*endcol < 0) /* passive object */ + { + attrPassive->setG0G2=iData & 0x3f; + PageAtrb[offset] = *attrPassive; + } + else + PageAtrb[offset].setG0G2=iData & 0x3f; + break; + case 0x09: + PageChar[offset] = iData; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_G0P; /* FIXME: secondary? */ + attrPassive->setX26 = 1; + PageAtrb[offset] = *attrPassive; + } + else + { + PageAtrb[offset].charset = C_G0P; /* FIXME: secondary? */ + PageAtrb[offset].setX26 = 1; + } + break; +// case 0x0b: (see 0x02) + case 0x0c: + { + int conc = (iData & 0x04); + int inv = (iData & 0x10); + int dw = (iData & 0x40 ?1:0); + int dh = (iData & 0x01 ?1:0); + int sep = (iData & 0x20); + int bw = (iData & 0x02 ?1:0); + if (*endcol < 0) /* passive object */ + { + if (conc) + { + attrPassive->concealed = 1; + attrPassive->fg = attrPassive->bg; + } + attrPassive->inverted = (inv ? 1- attrPassive->inverted : 0); + attrPassive->doubleh = dh; + attrPassive->doublew = dw; + attrPassive->boxwin = bw; + if (bw) attrPassive->IgnoreAtBlackBgSubst = 0; + if (sep) + { + if (attrPassive->charset == C_G1C) + attrPassive->charset = C_G1S; + else + attrPassive->underline = 1; + } + else + { + if (attrPassive->charset == C_G1S) + attrPassive->charset = C_G1C; + else + attrPassive->underline = 0; + } + } + else + { + + int c = *pAPx0 + (*endcol == 40 ? *pAPx : 0); /* current column */ + int c1 = offset; + TextPageAttr_t *p = &PageAtrb[offset]; + do + { + p->inverted = (inv ? 1- p->inverted : 0); + if (conc) + { + p->concealed = 1; + p->fg = p->bg; + } + if (sep) + { + if (p->charset == C_G1C) + p->charset = C_G1S; + else + p->underline = 1; + } + else + { + if (p->charset == C_G1S) + p->charset = C_G1C; + else + p->underline = 0; + } + p->doublew = dw; + p->doubleh = dh; + p->boxwin = bw; + if (bw) p->IgnoreAtBlackBgSubst = 0; + p++; + c++; + c1++; + } while (c < *endcol); + } + break; + } + case 0x0d: + PageChar[offset] = iData & 0x3f; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp); + PageAtrb[offset] = *attrPassive; + } + else + PageAtrb[offset].charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp); + break; + case 0x0f: + PageChar[offset] = iData; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_G2; + PageAtrb[offset] = *attrPassive; + } + else + PageAtrb[offset].charset = C_G2; + break; + default: + if (iMode == 0x10 && iData == 0x2a) + iData = '@'; + if (iMode >= 0x10) + { + PageChar[offset] = iData; + if (*endcol < 0) /* passive object */ + { + attrPassive->charset = C_G0P; + attrPassive->diacrit = iMode & 0x0f; + attrPassive->setX26 = 1; + PageAtrb[offset] = *attrPassive; + } + else + { + PageAtrb[offset].charset = C_G0P; + PageAtrb[offset].diacrit = iMode & 0x0f; + PageAtrb[offset].setX26 = 1; + } + } + break; /* unsupported or not yet implemented mode: ignore */ + } /* switch (iMode) */ + } + else /* ================= (iAddress >= 40): row addresses ====================== */ + { + switch (iMode) + { + case 0x00: + if (0 == (iData>>5)) + { + m_txtCache->FullScrColor = iData & 0x1f; + } + break; + case 0x01: + if (*endcol == 40) /* active object */ + { + *pAPy = RowAddress2Row(iAddress); /* new Active Row */ + + int color = iData & 0x1f; + int row = *pAPy0 + *pAPy; + int maxrow; + + if (row <= 24 && 0 == (iData>>5)) + maxrow = row; + else if (3 == (iData>>5)) + maxrow = 24; + else + maxrow = -1; + for (; row <= maxrow; row++) + m_txtCache->FullRowColor[row] = color; + *endcol = -1; + } + break; + case 0x04: + *pAPy = RowAddress2Row(iAddress); /* new Active Row */ + if (iData < 40) + *pAPx = iData; /* new Active Column */ + *endcol = -1; /* FIXME: check if row changed? */ + break; + case 0x07: + if (iAddress == 0x3f) + { + *pAPx = *pAPy = 0; /* new Active Position 0,0 */ + if (*endcol == 40) /* active object */ + { + int color = iData & 0x1f; + int row = *pAPy0; // + *pAPy; + int maxrow; + + if (row <= 24 && 0 == (iData>>5)) + maxrow = row; + else if (3 == (iData>>5)) + maxrow = 24; + else + maxrow = -1; + for (; row <= maxrow; row++) + m_txtCache->FullRowColor[row] = color; + } + *endcol = -1; + } + break; + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + /* ignore */ + break; + case 0x10: + m_txtCache->tAPy = iAddress - 40; + m_txtCache->tAPx = iData; + break; + case 0x11: + case 0x12: + case 0x13: + if (iAddress & 0x10) /* POP or GPOP */ + { + unsigned char APx = 0, APy = 0; + unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy; + int triplet = 3 * ((iData >> 5) & 0x03) + (iMode & 0x03); + int packet = (iAddress & 0x03) + 1; + int subp = iData & 0x0f; + int high = (iData >> 4) & 0x01; + + + if (APx0 < 40) /* not in side panel */ + { + Eval_NumberedObject((iAddress & 0x08) ? m_txtCache->gpop : m_txtCache->pop, subp, packet, triplet, high, &APx, &APy, &APx0, &APy0, PageChar,PageAtrb); + } + } + else if (iAddress & 0x08) /* local: eval invoked object */ + { + unsigned char APx = 0, APy = 0; + unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy; + int descode = ((iAddress & 0x01) << 3) | (iData >> 4); + int triplet = iData & 0x0f; + + if (APx0 < 40) /* not in side panel */ + { + Eval_Object(13 * 23 + 13 * descode + triplet, pstCachedPage, &APx, &APy, &APx0, &APy0, (tObjType)(triplet % 3), pagedata, PageChar, PageAtrb); + } + } + break; + case 0x15: + case 0x16: + case 0x17: + if (0 == (iAddress & 0x08)) /* Object Definition illegal or only level 3.5 */ + break; /* ignore */ + + m_txtCache->tAPx = m_txtCache->tAPy = 0; + *endcol = -1; + return 0xFF; /* termination by object definition */ + break; + case 0x18: + if (0 == (iData & 0x10)) /* DRCS Mode reserved or only level 3.5 */ + break; /* ignore */ + + if (iData & 0x40) + *drcssubp = iData & 0x0f; + else + *gdrcssubp = iData & 0x0f; + break; + case 0x1f: + m_txtCache->tAPx = m_txtCache->tAPy = 0; + *endcol = -1; + return 0x80 | iData; /* explicit termination */ + break; + default: + break; /* unsupported or not yet implemented mode: ignore */ + } /* switch (iMode) */ + } /* (iAddress >= 40): row addresses */ + + if (iAddress < 40 || iMode != 0x10) /* leave temp. AP-Offset unchanged only immediately after definition */ + m_txtCache->tAPx = m_txtCache->tAPy = 0; + + return 0; /* normal exit, no termination */ +} + +/* get object data */ +/* in: absolute triplet number (0..506, start at packet 3 byte 1) */ +/* in: pointer to cache struct of page data */ +/* out: 18 bit triplet data, <0 if invalid number, not cached, or hamming error */ +int CTeletextDecoder::iTripletNumber2Data(int iONr, TextCachedPage_t *pstCachedPage, unsigned char* pagedata) +{ + if (iONr > 506 || 0 == pstCachedPage) + return -1; + + unsigned char *p; + int packet = (iONr / 13) + 3; + int packetoffset = 3 * (iONr % 13); + + if (packet <= 23) + p = pagedata + 40*(packet-1) + packetoffset + 1; + else if (packet <= 25) + { + if (0 == pstCachedPage->pageinfo.p24) + return -1; + p = pstCachedPage->pageinfo.p24 + 40*(packet-24) + packetoffset + 1; + } + else + { + int descode = packet - 26; + if (0 == pstCachedPage->pageinfo.ext) + return -1; + if (0 == pstCachedPage->pageinfo.ext->p26[descode]) + return -1; + p = pstCachedPage->pageinfo.ext->p26[descode] + packetoffset; /* first byte (=designation code) is not cached */ + } + return CDVDTeletextTools::deh24(p); +} + +int CTeletextDecoder::SetNational(unsigned char sec) +{ + switch (sec) + { + case 0x08: + return NAT_PL; //polish + case 0x16: + case 0x36: + return NAT_TR; //turkish + case 0x1d: + return NAT_SR; //serbian, croatian, slovenian + case 0x20: + return NAT_SC; // serbian, croatian + case 0x24: + return NAT_RB; // russian, bulgarian + case 0x25: + return NAT_UA; // ukrainian + case 0x22: + return NAT_ET; // estonian + case 0x23: + return NAT_LV; // latvian, lithuanian + case 0x37: + return NAT_GR; // greek + case 0x55: + return NAT_HB; // hebrew + case 0x47: + case 0x57: + return NAT_AR; // arabic + } + return CountryConversionTable[sec & 0x07]; +} + +int CTeletextDecoder::NextHex(int i) /* return next existing non-decimal page number */ +{ + int startpage = i; + if (startpage < 0x100) + startpage = 0x100; + + do + { + i++; + if (i > 0x8FF) + i = 0x100; + if (i == startpage) + break; + } while ((m_txtCache->SubPageTable[i] == 0xFF) || IsDec(i)); + return i; +} + +void CTeletextDecoder::SetColors(unsigned short *pcolormap, int offset, int number) +{ + int j = offset; /* index in global color table */ + + for (int i = 0; i < number; i++) + { + int r = ((pcolormap[i] >> 8) & 0xf) << 4; + int g = ((pcolormap[i] >> 4) & 0xf) << 4; + int b = ((pcolormap[i]) & 0xf) << 4; + + if (m_RenderInfo.rd0[j] != r) + { + m_RenderInfo.rd0[j] = r; + } + if (m_RenderInfo.gn0[j] != g) + { + m_RenderInfo.gn0[j] = g; + } + if (m_RenderInfo.bl0[j] != b) + { + m_RenderInfo.bl0[j] = b; + } + j++; + } +} + +color_t CTeletextDecoder::GetColorRGB(enumTeletextColor ttc) +{ + switch (ttc) + { + case TXT_ColorBlack: return 0xFF000000; + case TXT_ColorRed: return 0xFFFC1414; + case TXT_ColorGreen: return 0xFF24FC24; + case TXT_ColorYellow: return 0xFFFCC024; + case TXT_ColorBlue: return 0xFF0000FC; + case TXT_ColorMagenta: return 0xFFB000FC; + case TXT_ColorCyan: return 0xFF00FCFC; + case TXT_ColorWhite: return 0xFFFCFCFC; + case TXT_ColorTransp: return 0x00000000; + } + + /* Get colors for CLUTs 2+3 */ + int index = (int)ttc; + color_t color = (m_RenderInfo.tr0[index] << 24) | + (m_RenderInfo.bl0[index] << 16) | + (m_RenderInfo.gn0[index] << 8) | + m_RenderInfo.rd0[index]; + return color; +} diff --git a/xbmc/utils/Teletext.h b/xbmc/utils/Teletext.h new file mode 100644 index 0000000000..973d9fa08c --- /dev/null +++ b/xbmc/utils/Teletext.h @@ -0,0 +1,168 @@ +#pragma once + +/* + * Copyright (C) 2005-2009 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "TeletextDefines.h" +#include "Key.h" +#include "GUITexture.h" + +// stuff for freetype +#ifndef _LINUX +#include "ft2build.h" +#else +#include <ft2build.h> +#endif +#include FT_FREETYPE_H +#include FT_CACHE_H +#include FT_CACHE_SMALL_BITMAPS_H + +typedef enum /* object type */ +{ + OBJ_PASSIVE, + OBJ_ACTIVE, + OBJ_ADAPTIVE +} tObjType; + +class CTeletextDecoder +{ +public: + CTeletextDecoder(); + virtual ~CTeletextDecoder(void); + + bool NeedRendering() { return m_updateTexture; } + void RenderingDone() { m_updateTexture = false; } + color_t *GetTextureBuffer() { return m_TextureBuffer + (m_RenderInfo.Width*m_YOffset); } + int GetHeight() { return m_RenderInfo.Height; } + int GetWidth() { return m_RenderInfo.Width; } + bool InitDecoder(); + void EndDecoder(); + void RenderPage(); + bool HandleAction(const CAction &action); + +private: + void PageInput(int Number); + void GetNextPageOne(bool up); + void GetNextSubPage(int offset); + void SwitchZoomMode(); + void SwitchTranspMode(); + void SwitchHintMode(); + void ColorKey(int target); + void StartPageCatching(); + void StopPageCatching(); + void CatchNextPage(int firstlineinc, int inc); + void RenderCatchedPage(); + void DoFlashing(int startrow); + void DoRenderPage(int startrow, int national_subset_bak); + void Decode_BTT(); + void Decode_ADIP(); + int TopText_GetNext(int startpage, int up, int findgroup); + void Showlink(int column, int linkpage); + void CreateLine25(); + void RenderCharFB(int Char, TextPageAttr_t *Attribute); + void RenderCharBB(int Char, TextPageAttr_t *Attribute); + void CopyBB2FB(); + void SetFontWidth(int newWidth); + int GetCurFontWidth(); + void SetPosX(int column); + void ClearBB(color_t Color); + void ClearFB(color_t Color); + void FillBorder(color_t Color); + void FillRect(color_t *buffer, int xres, int x, int y, int w, int h, color_t Color); + void DrawVLine(color_t *lfb, int xres, int x, int y, int l, color_t color); + void DrawHLine(color_t *lfb, int xres,int x, int y, int l, color_t color); + void FillRectMosaicSeparated(color_t *lfb, int xres,int x, int y, int w, int h, color_t fgcolor, color_t bgcolor, int set); + void FillTrapez(color_t *lfb, int xres,int x0, int y0, int l0, int xoffset1, int h, int l1, color_t color); + void FlipHorz(color_t *lfb, int xres,int x, int y, int w, int h); + void FlipVert(color_t *lfb, int xres,int x, int y, int w, int h); + int ShapeCoord(int param, int curfontwidth, int curfontheight); + void 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); + void RenderDRCS(int xres, + unsigned char *s, /* pointer to char data, parity undecoded */ + color_t *d, /* pointer to frame buffer of top left pixel */ + unsigned char *ax, /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */ + color_t fgcolor, color_t bgcolor); + void RenderCharIntern(TextRenderInfo_t* RenderInfo, int Char, TextPageAttr_t *Attribute, int zoom, int yoffset); + int RenderChar(color_t *buffer, // pointer to render buffer, min. fontheight*2*xres + int xres, // length of 1 line in render buffer + int Char, // character to render + int *pPosX, // left border for rendering relative to *buffer, will be set to right border after rendering + int PosY, // vertical position of char in *buffer + TextPageAttr_t *Attribute, // Attributes of Char + bool zoom, // 1= character will be rendered in double height + int curfontwidth, // rendering width of character + int curfontwidth2, // rendering width of next character (needed for doublewidth) + int fontheight, // height of character + bool transpmode, // 1= transparent display + unsigned char *axdrcs, // width and height of DRCS-chars + int Ascender); + TextPageinfo_t* DecodePage(bool showl25, // 1=decode Level2.5-graphics + unsigned char* PageChar, // page buffer, min. 25*40 + TextPageAttr_t *PageAtrb, // attribut buffer, min 25*40 + bool HintMode, // 1=show hidden information + bool showflof); // 1=decode FLOF-line + void Eval_l25(unsigned char* page_char, TextPageAttr_t *PageAtrb, bool HintMode); + void Eval_Object(int iONr, TextCachedPage_t *pstCachedPage, + unsigned char *pAPx, unsigned char *pAPy, + unsigned char *pAPx0, unsigned char *pAPy0, + tObjType ObjType, unsigned char* pagedata, unsigned char* page_char, TextPageAttr_t* PageAtrb); + void Eval_NumberedObject(int p, int s, int packet, int triplet, int high, + unsigned char *pAPx, unsigned char *pAPy, + unsigned char *pAPx0, unsigned char *pAPy0, unsigned char* page_char, TextPageAttr_t* PageAtrb); + int Eval_Triplet(int iOData, TextCachedPage_t *pstCachedPage, + unsigned char *pAPx, unsigned char *pAPy, + unsigned char *pAPx0, unsigned char *pAPy0, + unsigned char *drcssubp, unsigned char *gdrcssubp, + signed char *endcol, TextPageAttr_t *attrPassive, unsigned char* pagedata, unsigned char* page_char, TextPageAttr_t* PageAtrb); + int iTripletNumber2Data(int iONr, TextCachedPage_t *pstCachedPage, unsigned char* pagedata); + int SetNational(unsigned char sec); + int NextHex(int i); + void SetColors(unsigned short *pcolormap, int offset, int number); + color_t GetColorRGB(enumTeletextColor ttc); + + static FT_Error MyFaceRequester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *aface); + + CStdString m_teletextFont; /* Path to teletext font */ + int m_YOffset; /* Swap position for Front buffer and Back buffer */ + color_t *m_TextureBuffer; /* Texture buffer to hold generated data */ + bool m_updateTexture; /* Update the texture if set */ + char prevHeaderPage; /* Needed for texture update if header is changed */ + char prevTimeSec; /* Needed for Time string update */ + + int m_CatchRow; /* for page catching */ + int m_CatchCol; /* " " " */ + int m_CatchedPage; /* " " " */ + int m_PCOldRow; /* " " " */ + int m_PCOldCol; /* " " " */ + + FT_Library m_Library; /* FreeType 2 data */ + FTC_Manager m_Manager; /* " " " */ + FTC_SBitCache m_Cache; /* " " " */ + FTC_SBit m_sBit; /* " " " */ + FT_Face m_Face; /* " " " */ + FTC_ImageTypeRec m_TypeTTF; /* " " " */ + int m_Ascender; /* " " " */ + + int m_TempPage; /* Temporary page number for number input */ + int m_LastPage; /* Last selected Page */ + TextCacheStruct_t* m_txtCache; /* Text cache generated by the DVDPLayer if Teletext present */ + TextRenderInfo_t m_RenderInfo; /* Rendering information of displayed Teletext page */ +}; diff --git a/xbmc/utils/TeletextDefines.h b/xbmc/utils/TeletextDefines.h new file mode 100644 index 0000000000..c5f375cadc --- /dev/null +++ b/xbmc/utils/TeletextDefines.h @@ -0,0 +1,485 @@ +#pragma once + +/* + * Copyright (C) 2005-2009 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StringUtils.h" + +#define FLOFSIZE 4 +#define PAGESIZE (40*25) +#define SUBTITLE_CACHESIZE 50 + +#define number2char(c) ((c) + (((c) <= 9) ? '0' : ('A' - 10))) + +enum /* indices in atrtable */ +{ + ATR_WB, /* white on black */ + ATR_PassiveDefault, /* Default for passive objects: white on black, ignore at Black Background Color Substitution */ + ATR_L250, /* line25 */ + ATR_L251, /* line25 */ + ATR_L252, /* line25 */ + ATR_L253, /* line25 */ + ATR_TOPMENU0, /* topmenu */ + ATR_TOPMENU1, /* topmenu */ + ATR_TOPMENU2, /* topmenu */ + ATR_TOPMENU3, /* topmenu */ + ATR_MSG0, /* message */ + ATR_MSG1, /* message */ + ATR_MSG2, /* message */ + ATR_MSG3, /* message */ + ATR_MSGDRM0, /* message */ + ATR_MSGDRM1, /* message */ + ATR_MSGDRM2, /* message */ + ATR_MSGDRM3, /* message */ + ATR_MENUHIL0, /* hilit menu line */ + ATR_MENUHIL1, /* hilit menu line */ + ATR_MENUHIL2, /* hilit menu line */ + ATR_MENU0, /* menu line */ + ATR_MENU1, /* menu line */ + ATR_MENU2, /* menu line */ + ATR_MENU3, /* menu line */ + ATR_MENU4, /* menu line */ + ATR_MENU5, /* menu line */ + ATR_MENU6, /* menu line */ + ATR_CATCHMENU0, /* catch menu line */ + ATR_CATCHMENU1 /* catch menu line */ +}; + +/* colortable */ +enum enumTeletextColor +{ + TXT_ColorBlack = 0, + TXT_ColorRed, /* 1 */ + TXT_ColorGreen, /* 2 */ + TXT_ColorYellow, /* 3 */ + TXT_ColorBlue, /* 4 */ + TXT_ColorMagenta, /* 5 */ + TXT_ColorCyan, /* 6 */ + TXT_ColorWhite, /* 7 */ + TXT_ColorMenu1 = (4*8), + TXT_ColorMenu2, + TXT_ColorMenu3, + TXT_ColorTransp, + TXT_ColorTransp2, + TXT_Color_SIZECOLTABLE +}; + +enum /* options for charset */ +{ + C_G0P = 0, /* primary G0 */ + C_G0S, /* secondary G0 */ + C_G1C, /* G1 contiguous */ + C_G1S, /* G1 separate */ + C_G2, + C_G3, + C_OFFSET_DRCS = 32 + /* 32..47: 32+subpage# GDRCS (offset/20 in PageChar) */ + /* 48..63: 48+subpage# DRCS (offset/20 in PageChar) */ +}; + +enum /* page function */ +{ + FUNC_LOP = 0, /* Basic Level 1 Teletext page (LOP) */ + FUNC_DATA, /* Data broadcasting page coded according to EN 300 708 [2] clause 4 */ + FUNC_GPOP, /* Global Object definition page (GPOP) - (see clause 10.5.1) */ + FUNC_POP, /* Normal Object definition page (POP) - (see clause 10.5.1) */ + FUNC_GDRCS, /* Global DRCS downloading page (GDRCS) - (see clause 10.5.2) */ + FUNC_DRCS, /* Normal DRCS downloading page (DRCS) - (see clause 10.5.2) */ + FUNC_MOT, /* Magazine Organization table (MOT) - (see clause 10.6) */ + FUNC_MIP, /* Magazine Inventory page (MIP) - (see clause 11.3) */ + FUNC_BTT, /* Basic TOP table (BTT) } */ + FUNC_AIT, /* Additional Information Table (AIT) } (see clause 11.2) */ + FUNC_MPT, /* Multi-page table (MPT) } */ + FUNC_MPTEX, /* Multi-page extension table (MPT-EX) } */ + FUNC_TRIGGER /* Page contain trigger messages defined according to [8] */ +}; + +enum +{ + NAT_DEFAULT = 0, + NAT_CZ = 1, + NAT_UK = 2, + NAT_ET = 3, + NAT_FR = 4, + NAT_DE = 5, + NAT_IT = 6, + NAT_LV = 7, + NAT_PL = 8, + NAT_SP = 9, + NAT_RO = 10, + NAT_SR = 11, + NAT_SW = 12, + NAT_TR = 13, + NAT_MAX_FROM_HEADER = 13, + NAT_SC = 14, + NAT_RB = 15, + NAT_UA = 16, + NAT_GR = 17, + NAT_HB = 18, + NAT_AR = 19 +}; + +const unsigned char CountryConversionTable[] = { NAT_UK, NAT_DE, NAT_SW, NAT_IT, NAT_FR, NAT_SP, NAT_CZ, NAT_RO}; +const unsigned char MapTblFG[] = { 0, 0, 8, 8, 16, 16, 16 }; +const unsigned char MapTblBG[] = { 8, 16, 8, 16, 8, 16, 24 }; +const unsigned short DefaultColors[] = /* 0x0bgr */ +{ + 0x000, 0x00f, 0x0f0, 0x0ff, 0xf00, 0xf0f, 0xff0, 0xfff, + 0x000, 0x007, 0x070, 0x077, 0x700, 0x707, 0x770, 0x777, + 0x50f, 0x07f, 0x7f0, 0xbff, 0xac0, 0x005, 0x256, 0x77c, + 0x333, 0x77f, 0x7f7, 0x7ff, 0xf77, 0xf7f, 0xff7, 0xddd, + 0x420, 0x210, 0x420, 0x000, 0x000 +}; + +/* hamming table */ +const unsigned char dehamming[] = +{ + 0x01, 0xFF, 0x01, 0x01, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0x02, 0x01, 0xFF, 0x0A, 0xFF, 0xFF, 0x07, + 0xFF, 0x00, 0x01, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x06, 0xFF, 0xFF, 0x0B, 0xFF, 0x00, 0x03, 0xFF, + 0xFF, 0x0C, 0x01, 0xFF, 0x04, 0xFF, 0xFF, 0x07, 0x06, 0xFF, 0xFF, 0x07, 0xFF, 0x07, 0x07, 0x07, + 0x06, 0xFF, 0xFF, 0x05, 0xFF, 0x00, 0x0D, 0xFF, 0x06, 0x06, 0x06, 0xFF, 0x06, 0xFF, 0xFF, 0x07, + 0xFF, 0x02, 0x01, 0xFF, 0x04, 0xFF, 0xFF, 0x09, 0x02, 0x02, 0xFF, 0x02, 0xFF, 0x02, 0x03, 0xFF, + 0x08, 0xFF, 0xFF, 0x05, 0xFF, 0x00, 0x03, 0xFF, 0xFF, 0x02, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0x03, + 0x04, 0xFF, 0xFF, 0x05, 0x04, 0x04, 0x04, 0xFF, 0xFF, 0x02, 0x0F, 0xFF, 0x04, 0xFF, 0xFF, 0x07, + 0xFF, 0x05, 0x05, 0x05, 0x04, 0xFF, 0xFF, 0x05, 0x06, 0xFF, 0xFF, 0x05, 0xFF, 0x0E, 0x03, 0xFF, + 0xFF, 0x0C, 0x01, 0xFF, 0x0A, 0xFF, 0xFF, 0x09, 0x0A, 0xFF, 0xFF, 0x0B, 0x0A, 0x0A, 0x0A, 0xFF, + 0x08, 0xFF, 0xFF, 0x0B, 0xFF, 0x00, 0x0D, 0xFF, 0xFF, 0x0B, 0x0B, 0x0B, 0x0A, 0xFF, 0xFF, 0x0B, + 0x0C, 0x0C, 0xFF, 0x0C, 0xFF, 0x0C, 0x0D, 0xFF, 0xFF, 0x0C, 0x0F, 0xFF, 0x0A, 0xFF, 0xFF, 0x07, + 0xFF, 0x0C, 0x0D, 0xFF, 0x0D, 0xFF, 0x0D, 0x0D, 0x06, 0xFF, 0xFF, 0x0B, 0xFF, 0x0E, 0x0D, 0xFF, + 0x08, 0xFF, 0xFF, 0x09, 0xFF, 0x09, 0x09, 0x09, 0xFF, 0x02, 0x0F, 0xFF, 0x0A, 0xFF, 0xFF, 0x09, + 0x08, 0x08, 0x08, 0xFF, 0x08, 0xFF, 0xFF, 0x09, 0x08, 0xFF, 0xFF, 0x0B, 0xFF, 0x0E, 0x03, 0xFF, + 0xFF, 0x0C, 0x0F, 0xFF, 0x04, 0xFF, 0xFF, 0x09, 0x0F, 0xFF, 0x0F, 0x0F, 0xFF, 0x0E, 0x0F, 0xFF, + 0x08, 0xFF, 0xFF, 0x05, 0xFF, 0x0E, 0x0D, 0xFF, 0xFF, 0x0E, 0x0F, 0xFF, 0x0E, 0x0E, 0xFF, 0x0E +}; + +/* odd parity table, error=0x20 (space) */ +const unsigned char deparity[] = +{ + ' ' , 0x01, 0x02, ' ' , 0x04, ' ' , ' ' , 0x07, 0x08, ' ' , ' ' , 0x0b, ' ' , 0x0d, 0x0e, ' ' , + 0x10, ' ' , ' ' , 0x13, ' ' , 0x15, 0x16, ' ' , ' ' , 0x19, 0x1a, ' ' , 0x1c, ' ' , ' ' , 0x1f, + 0x20, ' ' , ' ' , 0x23, ' ' , 0x25, 0x26, ' ' , ' ' , 0x29, 0x2a, ' ' , 0x2c, ' ' , ' ' , 0x2f, + ' ' , 0x31, 0x32, ' ' , 0x34, ' ' , ' ' , 0x37, 0x38, ' ' , ' ' , 0x3b, ' ' , 0x3d, 0x3e, ' ' , + 0x40, ' ' , ' ' , 0x43, ' ' , 0x45, 0x46, ' ' , ' ' , 0x49, 0x4a, ' ' , 0x4c, ' ' , ' ' , 0x4f, + ' ' , 0x51, 0x52, ' ' , 0x54, ' ' , ' ' , 0x57, 0x58, ' ' , ' ' , 0x5b, ' ' , 0x5d, 0x5e, ' ' , + ' ' , 0x61, 0x62, ' ' , 0x64, ' ' , ' ' , 0x67, 0x68, ' ' , ' ' , 0x6b, ' ' , 0x6d, 0x6e, ' ' , + 0x70, ' ' , ' ' , 0x73, ' ' , 0x75, 0x76, ' ' , ' ' , 0x79, 0x7a, ' ' , 0x7c, ' ' , ' ' , 0x7f, + 0x00, ' ' , ' ' , 0x03, ' ' , 0x05, 0x06, ' ' , ' ' , 0x09, 0x0a, ' ' , 0x0c, ' ' , ' ' , 0x0f, + ' ' , 0x11, 0x12, ' ' , 0x14, ' ' , ' ' , 0x17, 0x18, ' ' , ' ' , 0x1b, ' ' , 0x1d, 0x1e, ' ' , + ' ' , 0x21, 0x22, ' ' , 0x24, ' ' , ' ' , 0x27, 0x28, ' ' , ' ' , 0x2b, ' ' , 0x2d, 0x2e, ' ' , + 0x30, ' ' , ' ' , 0x33, ' ' , 0x35, 0x36, ' ' , ' ' , 0x39, 0x3a, ' ' , 0x3c, ' ' , ' ' , 0x3f, + ' ' , 0x41, 0x42, ' ' , 0x44, ' ' , ' ' , 0x47, 0x48, ' ' , ' ' , 0x4b, ' ' , 0x4d, 0x4e, ' ' , + 0x50, ' ' , ' ' , 0x53, ' ' , 0x55, 0x56, ' ' , ' ' , 0x59, 0x5a, ' ' , 0x5c, ' ' , ' ' , 0x5f, + 0x60, ' ' , ' ' , 0x63, ' ' , 0x65, 0x66, ' ' , ' ' , 0x69, 0x6a, ' ' , 0x6c, ' ' , ' ' , 0x6f, + ' ' , 0x71, 0x72, ' ' , 0x74, ' ' , ' ' , 0x77, 0x78, ' ' , ' ' , 0x7b, ' ' , 0x7d, 0x7e, ' ' , +}; + +/* + * [AleVT] + * + * This table generates the parity checks for hamm24/18 decoding. + * Bit 0 is for test A, 1 for B, ... + * + * Thanks to R. Gancarz for this fine table *g* + */ +const unsigned char hamm24par[3][256] = +{ + { + /* Parities of first byte */ + 0, 33, 34, 3, 35, 2, 1, 32, 36, 5, 6, 39, 7, 38, 37, 4, + 37, 4, 7, 38, 6, 39, 36, 5, 1, 32, 35, 2, 34, 3, 0, 33, + 38, 7, 4, 37, 5, 36, 39, 6, 2, 35, 32, 1, 33, 0, 3, 34, + 3, 34, 33, 0, 32, 1, 2, 35, 39, 6, 5, 36, 4, 37, 38, 7, + 39, 6, 5, 36, 4, 37, 38, 7, 3, 34, 33, 0, 32, 1, 2, 35, + 2, 35, 32, 1, 33, 0, 3, 34, 38, 7, 4, 37, 5, 36, 39, 6, + 1, 32, 35, 2, 34, 3, 0, 33, 37, 4, 7, 38, 6, 39, 36, 5, + 36, 5, 6, 39, 7, 38, 37, 4, 0, 33, 34, 3, 35, 2, 1, 32, + 40, 9, 10, 43, 11, 42, 41, 8, 12, 45, 46, 15, 47, 14, 13, 44, + 13, 44, 47, 14, 46, 15, 12, 45, 41, 8, 11, 42, 10, 43, 40, 9, + 14, 47, 44, 13, 45, 12, 15, 46, 42, 11, 8, 41, 9, 40, 43, 10, + 43, 10, 9, 40, 8, 41, 42, 11, 15, 46, 45, 12, 44, 13, 14, 47, + 15, 46, 45, 12, 44, 13, 14, 47, 43, 10, 9, 40, 8, 41, 42, 11, + 42, 11, 8, 41, 9, 40, 43, 10, 14, 47, 44, 13, 45, 12, 15, 46, + 41, 8, 11, 42, 10, 43, 40, 9, 13, 44, 47, 14, 46, 15, 12, 45, + 12, 45, 46, 15, 47, 14, 13, 44, 40, 9, 10, 43, 11, 42, 41, 8 + }, { + /* Parities of second byte */ + 0, 41, 42, 3, 43, 2, 1, 40, 44, 5, 6, 47, 7, 46, 45, 4, + 45, 4, 7, 46, 6, 47, 44, 5, 1, 40, 43, 2, 42, 3, 0, 41, + 46, 7, 4, 45, 5, 44, 47, 6, 2, 43, 40, 1, 41, 0, 3, 42, + 3, 42, 41, 0, 40, 1, 2, 43, 47, 6, 5, 44, 4, 45, 46, 7, + 47, 6, 5, 44, 4, 45, 46, 7, 3, 42, 41, 0, 40, 1, 2, 43, + 2, 43, 40, 1, 41, 0, 3, 42, 46, 7, 4, 45, 5, 44, 47, 6, + 1, 40, 43, 2, 42, 3, 0, 41, 45, 4, 7, 46, 6, 47, 44, 5, + 44, 5, 6, 47, 7, 46, 45, 4, 0, 41, 42, 3, 43, 2, 1, 40, + 48, 25, 26, 51, 27, 50, 49, 24, 28, 53, 54, 31, 55, 30, 29, 52, + 29, 52, 55, 30, 54, 31, 28, 53, 49, 24, 27, 50, 26, 51, 48, 25, + 30, 55, 52, 29, 53, 28, 31, 54, 50, 27, 24, 49, 25, 48, 51, 26, + 51, 26, 25, 48, 24, 49, 50, 27, 31, 54, 53, 28, 52, 29, 30, 55, + 31, 54, 53, 28, 52, 29, 30, 55, 51, 26, 25, 48, 24, 49, 50, 27, + 50, 27, 24, 49, 25, 48, 51, 26, 30, 55, 52, 29, 53, 28, 31, 54, + 49, 24, 27, 50, 26, 51, 48, 25, 29, 52, 55, 30, 54, 31, 28, 53, + 28, 53, 54, 31, 55, 30, 29, 52, 48, 25, 26, 51, 27, 50, 49, 24 + }, { + /* Parities of third byte */ + 63, 14, 13, 60, 12, 61, 62, 15, 11, 58, 57, 8, 56, 9, 10, 59, + 10, 59, 56, 9, 57, 8, 11, 58, 62, 15, 12, 61, 13, 60, 63, 14, + 9, 56, 59, 10, 58, 11, 8, 57, 61, 12, 15, 62, 14, 63, 60, 13, + 60, 13, 14, 63, 15, 62, 61, 12, 8, 57, 58, 11, 59, 10, 9, 56, + 8, 57, 58, 11, 59, 10, 9, 56, 60, 13, 14, 63, 15, 62, 61, 12, + 61, 12, 15, 62, 14, 63, 60, 13, 9, 56, 59, 10, 58, 11, 8, 57, + 62, 15, 12, 61, 13, 60, 63, 14, 10, 59, 56, 9, 57, 8, 11, 58, + 11, 58, 57, 8, 56, 9, 10, 59, 63, 14, 13, 60, 12, 61, 62, 15, + 31, 46, 45, 28, 44, 29, 30, 47, 43, 26, 25, 40, 24, 41, 42, 27, + 42, 27, 24, 41, 25, 40, 43, 26, 30, 47, 44, 29, 45, 28, 31, 46, + 41, 24, 27, 42, 26, 43, 40, 25, 29, 44, 47, 30, 46, 31, 28, 45, + 28, 45, 46, 31, 47, 30, 29, 44, 40, 25, 26, 43, 27, 42, 41, 24, + 40, 25, 26, 43, 27, 42, 41, 24, 28, 45, 46, 31, 47, 30, 29, 44, + 29, 44, 47, 30, 46, 31, 28, 45, 41, 24, 27, 42, 26, 43, 40, 25, + 30, 47, 44, 29, 45, 28, 31, 46, 42, 27, 24, 41, 25, 40, 43, 26, + 43, 26, 25, 40, 24, 41, 42, 27, 31, 46, 45, 28, 44, 29, 30, 47 + } +}; + +/* + * [AleVT] + * + * Table to extract the lower 4 bit from hamm24/18 encoded bytes + */ +const unsigned char hamm24val[256] = +{ + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, + 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, 8, 9, 9, 9, 9, + 10, 10, 10, 10, 11, 11, 11, 11, 10, 10, 10, 10, 11, 11, 11, 11, + 12, 12, 12, 12, 13, 13, 13, 13, 12, 12, 12, 12, 13, 13, 13, 13, + 14, 14, 14, 14, 15, 15, 15, 15, 14, 14, 14, 14, 15, 15, 15, 15, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, + 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, 8, 9, 9, 9, 9, + 10, 10, 10, 10, 11, 11, 11, 11, 10, 10, 10, 10, 11, 11, 11, 11, + 12, 12, 12, 12, 13, 13, 13, 13, 12, 12, 12, 12, 13, 13, 13, 13, + 14, 14, 14, 14, 15, 15, 15, 15, 14, 14, 14, 14, 15, 15, 15, 15 +}; + +const signed char hamm24err[64] = +{ + 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +/* + * [AleVT] + * + * Mapping from parity checks made by table hamm24par to faulty bit + * in the decoded 18 bit word. + */ +const unsigned int hamm24cor[64] = +{ + 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, + 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, + 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, + 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, + 0x00000, 0x00000, 0x00000, 0x00001, 0x00000, 0x00002, 0x00004, 0x00008, + 0x00000, 0x00010, 0x00020, 0x00040, 0x00080, 0x00100, 0x00200, 0x00400, + 0x00000, 0x00800, 0x01000, 0x02000, 0x04000, 0x08000, 0x10000, 0x20000, + 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, +}; + +inline int IsDec(int i) +{ + return ((i & 0x00F) <= 9) && ((i & 0x0F0) <= 0x90); +} + +/* struct for page attributes */ +typedef struct +{ + unsigned char fg :6; /* foreground color */ + unsigned char bg :6; /* background color */ + unsigned char charset :6; /* see enum above */ + unsigned char doubleh :1; /* double height */ + unsigned char doublew :1; /* double width */ + /* ignore at Black Background Color Substitution */ + /* black background set by New Background ($1d) instead of start-of-row default or Black Backgr. ($1c) */ + /* or black background set by level 2.5 extensions */ + unsigned char IgnoreAtBlackBgSubst:1; + unsigned char concealed:1; /* concealed information */ + unsigned char inverted :1; /* colors inverted */ + unsigned char flashing :5; /* flash mode */ + unsigned char diacrit :4; /* diacritical mark */ + unsigned char underline:1; /* Text underlined */ + unsigned char boxwin :1; /* Text boxed/windowed */ + unsigned char setX26 :1; /* Char is set by packet X/26 (no national subset used) */ + unsigned char setG0G2 :7; /* G0+G2 set designation */ +} TextPageAttr_t; + +/* struct for (G)POP/(G)DRCS links for level 2.5, allocated at reception of p27/4 or /5, initialized with 0 after allocation */ +typedef struct +{ + unsigned short page; /* linked page number */ + unsigned short subpage; /* 1 bit for each needed (1) subpage */ + unsigned char l25:1; /* 1: page required at level 2.5 */ + unsigned char l35:1; /* 1: page required at level 3.5 */ + unsigned char drcs:1; /* 1: link to (G)DRCS, 0: (G)POP */ + unsigned char local:1; /* 1: global (G*), 0: local */ +} Textp27_t; + +/* struct for extension data for level 2.5, allocated at reception, initialized with 0 after allocation */ +typedef struct +{ + unsigned char *p26[16]; /* array of pointers to max. 16 designation codes of packet 26 */ + Textp27_t *p27; /* array of 4 structs for (G)POP/(G)DRCS links for level 2.5 */ + unsigned short bgr[16]; /* CLUT 2+3, 2*8 colors, 0x0bgr */ + unsigned char DefaultCharset:7; /* default G0/G2 charset + national option */ + unsigned char LSP:1; /* 1: left side panel to be displayed */ + unsigned char SecondCharset:7; /* second G0 charset */ + unsigned char RSP:1; /* 1: right side panel to be displayed */ + unsigned char DefScreenColor:5; /* default screen color (above and below lines 0..24) */ + unsigned char ColorTableRemapping:3; /* 1: index in table of CLUTs to use */ + unsigned char DefRowColor:5; /* default row color (left and right to lines 0..24) */ + unsigned char BlackBgSubst:1; /* 1: substitute black background (as result of start-of-line or 1c, not 00/10+1d) */ + unsigned char SPL25:1; /* 1: side panel required at level 2.5 */ + unsigned char p28Received:1; /* 1: extension data valid (p28/0 received) */ + unsigned char LSPColumns:4; /* number of columns in left side panel, 0->16, rsp=16-lsp */ +} TextExtData_t; + +/* struct for pageinfo, max. 16 Bytes, at beginning of each cached page buffer, initialized with 0 after allocation */ +typedef struct +{ + unsigned char *p24; /* pointer to lines 25+26 (packets 24+25) (2*40 bytes) for FLOF or level 2.5 data */ + TextExtData_t *ext; /* pointer to array[16] of data for level 2.5 */ + unsigned char boxed :1; /* p0: boxed (newsflash or subtitle) */ + unsigned char nationalvalid :1; /* p0: national option character subset is valid (no biterror detected) */ + unsigned char national :3; /* p0: national option character subset */ + unsigned char function :3; /* p28/0: page function */ +} TextPageinfo_t; + +/* one cached page: struct for pageinfo, 24 lines page data */ +typedef struct +{ + TextPageinfo_t pageinfo; + unsigned char p0[24]; /* packet 0: center of headline */ + unsigned char data[23*40]; /* packet 1-23 */ +} TextCachedPage_t; + +typedef struct +{ + short page; + short language; +} TextSubtitle_t; + +typedef struct +{ + bool Valid; + long Timestamp; + unsigned char PageChar[40 * 25]; + TextPageAttr_t PageAtrb[40 * 25]; +} TextSubtitleCache_t; + +/* main data structure */ +typedef struct TextCacheStruct_t +{ + int CurrentPage[9]; + int CurrentSubPage[9]; + TextExtData_t *astP29[9]; + TextCachedPage_t *astCachetable[0x900][0x80]; + unsigned char SubPageTable[0x900]; + unsigned char BasicTop[0x900]; + short FlofPages[0x900][FLOFSIZE]; + char ADIPTable[0x900][13]; + int ADIP_PgMax; + int ADIP_Pg[10]; + bool BTTok; + int CachedPages; + int PageReceiving; + int Page; + int SubPage; + bool PageUpdate; + int NationalSubset; + int NationalSubsetSecondary; + bool ZapSubpageManual; + TextSubtitle_t SubtitlePages[8]; + unsigned char TimeString[8]; + int vtxtpid; + + /* cachetable for packets 29 (one for each magazine) */ + /* cachetable */ + unsigned char FullRowColor[25]; + unsigned char FullScrColor; + unsigned char tAPx, tAPy; /* temporary offset to Active Position for objects */ + short pop, gpop, drcs, gdrcs; + unsigned short *ColorTable; + + CStdString line30; +} TextCacheStruct_t; + +/* struct for all Information needed for Page Rendering */ +typedef struct +{ + bool PageCatching; + bool TranspMode; + bool HintMode; + bool ShowFlof; + bool Show39; + bool Showl25; + bool ShowHex; + int ZoomMode; + + int InputCounter; + int ClearBBColor; + int Prev_100, Prev_10, Next_10, Next_100; + int Height; + int Width; + int FontHeight; + int FontWidth; + int FontWidth_Normal; + unsigned short rd0[TXT_Color_SIZECOLTABLE]; + unsigned short gn0[TXT_Color_SIZECOLTABLE]; + unsigned short bl0[TXT_Color_SIZECOLTABLE]; + unsigned short tr0[TXT_Color_SIZECOLTABLE]; + TextSubtitleCache_t *SubtitleCache[SUBTITLE_CACHESIZE]; + unsigned char PageChar[25*40]; + TextPageAttr_t PageAtrb[25*40]; + TextPageinfo_t *PageInfo; + int PosX; + int PosY; + int nofirst; + unsigned char axdrcs[12+1+10+1]; + int TTFShiftX, TTFShiftY; /* parameters for adapting to various TTF fonts */ + bool Boxed; + int ScreenMode, PrevScreenMode; + bool DelayStarted; + unsigned int SubtitleDelay; +} TextRenderInfo_t; + +class CDVDTeletextTools +{ +public: + static void NextDec(int *i); + static void PrevDec(int *i); + static void Hex2Str(char *s, unsigned int n); + static signed int deh24(unsigned char *p); +}; |