summaryrefslogtreecommitdiff
path: root/vdr/extensions/vdr-1.6.0-2_extensions.diff
diff options
context:
space:
mode:
Diffstat (limited to 'vdr/extensions/vdr-1.6.0-2_extensions.diff')
-rw-r--r--vdr/extensions/vdr-1.6.0-2_extensions.diff27399
1 files changed, 27399 insertions, 0 deletions
diff --git a/vdr/extensions/vdr-1.6.0-2_extensions.diff b/vdr/extensions/vdr-1.6.0-2_extensions.diff
new file mode 100644
index 0000000..b168bab
--- /dev/null
+++ b/vdr/extensions/vdr-1.6.0-2_extensions.diff
@@ -0,0 +1,27399 @@
+diff -ruNp vdr-1.6.0-2/channels.c vdr-1.6.0-2-extensions/channels.c
+--- vdr-1.6.0-2/channels.c 2008-03-05 17:42:50.000000000 +0100
++++ vdr-1.6.0-2-extensions/channels.c 2009-04-09 20:48:26.000000000 +0200
+@@ -13,6 +13,9 @@
+ #include "device.h"
+ #include "epg.h"
+ #include "timers.h"
++#ifdef USE_STREAMDEVEXT
++#include "status.h"
++#endif /* STREAMDEVEXT */
+
+ // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
+ // format characters in order to allow any number of blanks after a numeric
+@@ -51,6 +54,9 @@ const tChannelParameterMap CoderateValue
+
+ const tChannelParameterMap ModulationValues[] = {
+ { 0, QPSK },
++#ifdef USE_ATSC
++ { 8, VSB_8 },
++#endif /* ATSC */
+ { 16, QAM_16 },
+ { 32, QAM_32 },
+ { 64, QAM_64 },
+@@ -166,6 +172,9 @@ cChannel::cChannel(void)
+ shortName = strdup("");
+ provider = strdup("");
+ portalName = strdup("");
++#ifdef USE_PLUGINPARAM
++ pluginParam = strdup("");
++#endif /* PLUGINPARAM */
+ memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);
+ inversion = INVERSION_AUTO;
+ bandwidth = BANDWIDTH_AUTO;
+@@ -187,6 +196,9 @@ cChannel::cChannel(const cChannel &Chann
+ shortName = NULL;
+ provider = NULL;
+ portalName = NULL;
++#ifdef USE_PLUGINPARAM
++ pluginParam = NULL;
++#endif /* PLUGINPARAM */
+ schedule = NULL;
+ linkChannels = NULL;
+ refChannel = NULL;
+@@ -215,6 +227,9 @@ cChannel::~cChannel()
+ free(shortName);
+ free(provider);
+ free(portalName);
++#ifdef USE_PLUGINPARAM
++ free(pluginParam);
++#endif /* PLUGINPARAM */
+ }
+
+ cChannel& cChannel::operator= (const cChannel &Channel)
+@@ -223,6 +238,9 @@ cChannel& cChannel::operator= (const cCh
+ shortName = strcpyrealloc(shortName, Channel.shortName);
+ provider = strcpyrealloc(provider, Channel.provider);
+ portalName = strcpyrealloc(portalName, Channel.portalName);
++#ifdef USE_PLUGINPARAM
++ pluginParam = strcpyrealloc(pluginParam, Channel.pluginParam);
++#endif /* PLUGINPARAM */
+ memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__);
+ return *this;
+ }
+@@ -280,9 +298,30 @@ void cChannel::CopyTransponderData(const
+ transmission = Channel->transmission;
+ guard = Channel->guard;
+ hierarchy = Channel->hierarchy;
++#ifdef USE_PLUGINPARAM
++ if (IsPlug()) pluginParam = strcpyrealloc(pluginParam, Channel->pluginParam);
++#endif /* PLUGINPARAM */
+ }
+ }
+
++#ifdef USE_PLUGINPARAM
++bool cChannel::SetPlugTransponderData(int Source, int Frequency, const char *PluginParam)
++{
++ if (source != Source || frequency != Frequency || (strcmp(pluginParam, PluginParam) != 0)) {
++ if (Number()) {
++ dsyslog("changing transponder data of channel %d from %s:%d:%s to %s:%d:%s", Number(), *cSource::ToString(source), frequency, pluginParam, *cSource::ToString(Source), Frequency, PluginParam);
++ modification |= CHANNELMOD_TRANSP;
++ Channels.SetModified();
++ }
++ source = Source;
++ frequency = Frequency;
++ pluginParam = strcpyrealloc(pluginParam, PluginParam);
++ schedule = NULL;
++ }
++ return true;
++}
++#endif /* PLUGINPARAM */
++
+ bool cChannel::SetSatTransponderData(int Source, int Frequency, char Polarization, int Srate, int CoderateH)
+ {
+ // Workarounds for broadcaster stupidity:
+@@ -382,6 +421,9 @@ void cChannel::SetName(const char *Name,
+ if (nn || ns || np) {
+ if (Number()) {
+ dsyslog("changing name of channel %d from '%s,%s;%s' to '%s,%s;%s'", Number(), name, shortName, provider, Name, ShortName, Provider);
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgChannelChange(this, scMod);
++#endif /* STREAMDEVEXT */
+ modification |= CHANNELMOD_NAME;
+ Channels.SetModified();
+ }
+@@ -407,6 +449,20 @@ void cChannel::SetPortalName(const char
+ }
+ }
+
++#ifdef USE_PLUGINPARAM
++void cChannel::SetPluginParam(const char *PluginParam)
++{
++ if (!isempty(PluginParam) && strcmp(pluginParam, PluginParam) != 0) {
++ if (Number()) {
++ dsyslog("changing plugin parameters of channel %d from '%s' to '%s'", Number(), pluginParam, PluginParam);
++ modification |= CHANNELMOD_TRANSP;
++ Channels.SetModified();
++ }
++ pluginParam = strcpyrealloc(pluginParam, PluginParam);
++ }
++}
++#endif /* PLUGINPARAM */
++
+ #define STRDIFF 0x01
+ #define VALDIFF 0x02
+
+@@ -593,7 +649,11 @@ cString cChannel::ParametersToString(voi
+ if (isdigit(type))
+ type = 'S';
+ #define ST(s) if (strchr(s, type))
++#ifdef USE_PLUGINPARAM
++ char buffer[256];
++#else
+ char buffer[64];
++#endif /* PLUGINPARAM */
+ char *q = buffer;
+ *q = 0;
+ ST(" S ") q += sprintf(q, "%c", polarization);
+@@ -605,6 +665,9 @@ cString cChannel::ParametersToString(voi
+ ST(" T") q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues));
+ ST(" T") q += PrintParameter(q, 'G', MapToUser(guard, GuardValues));
+ ST(" T") q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues));
++#ifdef USE_PLUGINPARAM
++ ST("P ") snprintf(buffer, sizeof(buffer), "%s", pluginParam);
++#endif /* PLUGINPARAM */
+ return buffer;
+ }
+
+@@ -626,7 +689,11 @@ static const char *ParseParameter(const
+
+ bool cChannel::StringToParameters(const char *s)
+ {
++#ifdef USE_PLUGINPARAM
++ while (s && *s && !IsPlug()) {
++#else
+ while (s && *s) {
++#endif /* PLUGINPARAM */
+ switch (toupper(*s)) {
+ case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break;
+ case 'C': s = ParseParameter(s, coderateH, CoderateValues); break;
+@@ -736,7 +803,11 @@ bool cChannel::Parse(const char *s)
+ dpids[0] = 0;
+ ok = false;
+ if (parambuf && sourcebuf && vpidbuf && apidbuf) {
++#ifdef USE_PLUGINPARAM
++ ok = ((source = cSource::FromString(sourcebuf)) >= 0) && StringToParameters(parambuf);
++#else
+ ok = StringToParameters(parambuf) && (source = cSource::FromString(sourcebuf)) >= 0;
++#endif /* PLUGINPARAM */
+
+ char *p = strchr(vpidbuf, '+');
+ if (p)
+@@ -827,6 +898,9 @@ bool cChannel::Parse(const char *s)
+ shortName = strcpyrealloc(shortName, p);
+ }
+ name = strcpyrealloc(name, namebuf);
++#ifdef USE_PLUGINPARAM
++ if (IsPlug()) pluginParam = strcpyrealloc(pluginParam, parambuf);
++#endif /* PLUGINPARAM */
+
+ free(parambuf);
+ free(sourcebuf);
+@@ -1072,6 +1146,9 @@ cChannel *cChannels::NewChannel(const cC
+ NewChannel->SetName(Name, ShortName, Provider);
+ Add(NewChannel);
+ ReNumber();
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgChannelChange(NewChannel, scAdd);
++#endif /* STREAMDEVEXT */
+ return NewChannel;
+ }
+ return NULL;
+diff -ruNp vdr-1.6.0-2/channels.h vdr-1.6.0-2-extensions/channels.h
+--- vdr-1.6.0-2/channels.h 2008-02-08 14:48:31.000000000 +0100
++++ vdr-1.6.0-2-extensions/channels.h 2009-04-09 20:48:26.000000000 +0200
+@@ -114,6 +114,9 @@ private:
+ char *shortName;
+ char *provider;
+ char *portalName;
++#ifdef USE_PLUGINPARAM
++ char *pluginParam;
++#endif /* PLUGINPARAM */
+ int __BeginData__;
+ int frequency; // MHz
+ int source;
+@@ -165,6 +168,9 @@ public:
+ int Frequency(void) const { return frequency; } ///< Returns the actual frequency, as given in 'channels.conf'
+ int Transponder(void) const; ///< Returns the transponder frequency in MHz, plus the polarization in case of sat
+ static int Transponder(int Frequency, char Polarization); ///< builds the transponder from the given Frequency and Polarization
++#ifdef USE_PLUGINPARAM
++ const char *PluginParam(void) const { return pluginParam; }
++#endif /* PLUGINPARAM */
+ int Source(void) const { return source; }
+ int Srate(void) const { return srate; }
+ int Vpid(void) const { return vpid; }
+@@ -199,6 +205,9 @@ public:
+ int Hierarchy(void) const { return hierarchy; }
+ const cLinkChannels* LinkChannels(void) const { return linkChannels; }
+ const cChannel *RefChannel(void) const { return refChannel; }
++#ifdef USE_PLUGINPARAM
++ bool IsPlug(void) const { return cSource::IsPlug(source); }
++#endif /* PLUGINPARAM */
+ bool IsCable(void) const { return cSource::IsCable(source); }
+ bool IsSat(void) const { return cSource::IsSat(source); }
+ bool IsTerr(void) const { return cSource::IsTerr(source); }
+@@ -206,12 +215,18 @@ public:
+ bool HasTimer(void) const;
+ int Modification(int Mask = CHANNELMOD_ALL);
+ void CopyTransponderData(const cChannel *Channel);
++#ifdef USE_PLUGINPARAM
++ bool SetPlugTransponderData(int Source, int Frequency, const char *PluginParam);
++#endif /* PLUGINPARAM */
+ bool SetSatTransponderData(int Source, int Frequency, char Polarization, int Srate, int CoderateH);
+ bool SetCableTransponderData(int Source, int Frequency, int Modulation, int Srate, int CoderateH);
+ bool SetTerrTransponderData(int Source, int Frequency, int Bandwidth, int Modulation, int Hierarchy, int CodeRateH, int CodeRateL, int Guard, int Transmission);
+ void SetId(int Nid, int Tid, int Sid, int Rid = 0);
+ void SetName(const char *Name, const char *ShortName, const char *Provider);
+ void SetPortalName(const char *PortalName);
++#ifdef USE_PLUGINPARAM
++ void SetPluginParam(const char *PluginParam);
++#endif /* PLUGINPARAM */
+ void SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid);
+ void SetCaIds(const int *CaIds); // list must be zero-terminated
+ void SetCaDescriptors(int Level);
+diff -ruNp vdr-1.6.0-2/config.c vdr-1.6.0-2-extensions/config.c
+--- vdr-1.6.0-2/config.c 2008-02-17 14:39:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/config.c 2009-04-09 20:48:26.000000000 +0200
+@@ -15,6 +15,9 @@
+ #include "interface.h"
+ #include "plugin.h"
+ #include "recording.h"
++#ifdef USE_SOURCECAPS
++#include "sources.h"
++#endif /* SOURCECAPS */
+
+ // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
+ // format characters in order to allow any number of blanks after a numeric
+@@ -28,18 +31,32 @@ cCommand::cCommand(void)
+ {
+ title = command = NULL;
+ confirm = false;
++#ifdef USE_CMDSUBMENU
++ nIndent = 0;
++ childs = NULL;
++#endif /* CMDSUBMENU */
+ }
+
+ cCommand::~cCommand()
+ {
+ free(title);
+ free(command);
++#ifdef USE_CMDSUBMENU
++ delete childs;
++#endif /* CMDSUBMENU */
+ }
+
+ bool cCommand::Parse(const char *s)
+ {
+ const char *p = strchr(s, ':');
+ if (p) {
++#ifdef USE_CMDSUBMENU
++ nIndent = 0;
++ while (*s == '-') {
++ nIndent++;
++ s++;
++ }
++#endif /* CMDSUBMENU */
+ int l = p - s;
+ if (l > 0) {
+ title = MALLOC(char, l + 1);
+@@ -85,6 +102,20 @@ const char *cCommand::Execute(const char
+ return result;
+ }
+
++#ifdef USE_CMDSUBMENU
++int cCommand::getChildCount(void)
++{
++ return childs ? childs->Count() : 0;
++}
++
++void cCommand::addChild(cCommand *newChild)
++{
++ if (!childs)
++ childs = new cCommands();
++ childs->AddConfig(newChild);
++}
++#endif /* CMDSUBMENU */
++
+ // --- cSVDRPhost ------------------------------------------------------------
+
+ cSVDRPhost::cSVDRPhost(void)
+@@ -125,6 +156,26 @@ bool cSVDRPhost::Accepts(in_addr_t Addre
+
+ cCommands Commands;
+ cCommands RecordingCommands;
++#ifdef USE_TIMERCMD
++cCommands TimerCommands;
++#endif /* TIMERCMD */
++
++#ifdef USE_CMDSUBMENU
++void cCommands::AddConfig(cCommand *Object)
++{
++ if (!Object)
++ return;
++ //isyslog ("Indent %d %s\n", Object->getIndent(), Object->Title());
++ for (int index = Count() - 1; index >= 0; index--) {
++ cCommand *parent = Get(index);
++ if (parent->getIndent() < Object->getIndent()) {
++ parent->addChild(Object);
++ return;
++ }
++ }
++ cConfig<cCommand>::Add(Object);
++}
++#endif /* CMDSUBMENU */
+
+ // --- cSVDRPhosts -----------------------------------------------------------
+
+@@ -216,6 +267,12 @@ cSetup::cSetup(void)
+ strcpy(OSDLanguage, ""); // default is taken from environment
+ strcpy(OSDSkin, "sttng");
+ strcpy(OSDTheme, "default");
++#ifdef USE_WAREAGLEICON
++ WarEagleIcons = 1;
++#endif /* WAREAGLEICON */
++#ifdef USE_VALIDINPUT
++ ShowValidInput = 0;
++#endif /* VALIDINPUT */
+ PrimaryDVB = 1;
+ ShowInfoOnChSwitch = 1;
+ TimeoutRequChInfo = 1;
+@@ -260,6 +317,18 @@ cSetup::cSetup(void)
+ VideoFormat = 0;
+ UpdateChannels = 5;
+ UseDolbyDigital = 1;
++#ifdef USE_DOLBYINREC
++ UseDolbyInRecordings = 1;
++#endif /* DOLBYINREC */
++#ifdef USE_DVBSETUP
++ DolbyTransferFix = 1;
++ ChannelBlocker = 0;
++ ChannelBlockerMode = 0;
++ ChannelBlockerList = strdup("");
++#endif /* DVBSETUP */
++#ifdef USE_SYNCEARLY
++ UseSyncEarlyPatch = 0;
++#endif /* SYNCEARLY */
+ ChannelInfoPos = 0;
+ ChannelInfoTime = 5;
+ OSDLeft = 54;
+@@ -276,24 +345,143 @@ cSetup::cSetup(void)
+ FontSmlSize = 18;
+ FontFixSize = 20;
+ MaxVideoFileSize = MAXVIDEOFILESIZE;
++#ifdef USE_HARDLINKCUTTER
++ MaxRecordingSize = DEFAULTRECORDINGSIZE;
++#endif /* HARDLINKCUTTER */
+ SplitEditedFiles = 0;
++#ifdef USE_HARDLINKCUTTER
++ HardLinkCutter = 0;
++#endif /* HARDLINKCUTTER */
++#ifdef USE_DELTIMESHIFTREC
++ DelTimeshiftRec = 0;
++#endif /* DELTIMESHIFTREC */
+ MinEventTimeout = 30;
+ MinUserInactivity = 300;
+ NextWakeupTime = 0;
+ MultiSpeedMode = 0;
+ ShowReplayMode = 0;
++#ifdef USE_DDEPGENTRY
++ DoubleEpgTimeDelta = 15;
++ DoubleEpgAction = 0;
++ MixEpgAction = 0;
++ DisableVPS = 0;
++#endif /* DDEPGENTRY */
+ ResumeID = 0;
++#ifdef USE_JUMPPLAY
++ JumpPlay = 0;
++ PlayJump = 0;
++ PauseLastMark = 0;
++ ReloadMarks = 0;
++#endif /* JUMPPLAY */
++#ifdef USE_SOURCECAPS
++ memset(SourceCaps, 0, sizeof SourceCaps);
++ SourceCapsSet = false;
++#endif /* SOURCECAPS */
+ CurrentChannel = -1;
+ CurrentVolume = MAXVOLUME;
+ CurrentDolby = 0;
+ InitialChannel = 0;
+ InitialVolume = -1;
++#ifdef USE_VOLCTRL
++ LRVolumeControl = 0;
++ LRChannelGroups = 1;
++ LRForwardRewind = 1;
++#endif /* VOLCTRL */
+ EmergencyExit = 1;
++#ifdef USE_NOEPG
++ noEPGMode = 0;
++ noEPGList = strdup("");
++#endif /* NOEPG */
++#ifdef USE_LIRCSETTINGS
++ LircRepeatDelay = 350;
++ LircRepeatFreq = 100;
++ LircRepeatTimeout = 500;
++#endif /* LIRCSETTINGS */
++#ifdef USE_LIEMIEXT
++ ShowRecDate = 1;
++ ShowRecTime = 1;
++ ShowRecLength = 0;
++ ShowProgressBar = 0;
++ MenuCmdPosition = 0;
++ JumpSeconds = 60;
++ JumpSecondsSlow = 10;
++ ShowTimerStop = 1;
++ MainMenuTitle = 0;
++ strcpy(CustomMainMenuTitle, "Video Disk Recorder");
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++ RecordingsSortMode = 0;
++ RecordingsSortDirsFirst = 0;
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ CutterAutoDelete = 0;
++#endif /* CUTTERQUEUE */
++#ifdef USE_CUTTIME
++ CutTime = 1;
++#endif /* CUTTIME */
++#ifdef USE_LIVEBUFFER
++ LiveBuffer = false;
++ KeepPaused = false;
++ LiveBufferSize = 100;
++ InRAM = false;
++ MemBufSize = 5;
++ ExtendBuffer = false;
++#endif /* LIVEBUFFER */
++#ifdef USE_DVDARCHIVE
++ DvdDisplayMode = 1;
++ DvdDisplayZeros = 1;
++ DvdTrayMode = 0;
++ DvdSpeedLimit = 0;
++#endif /* DVDARCHIVE */
++#ifdef USE_SOFTOSD
++ UseSoftOsd = 0;
++ SoftOsdRate = 50;
++ SoftOsdFadeinSteps = 6;
++ SoftOsdFadeoutSteps = 16;
++ SoftOsdPaletteOnly = 0;
++#endif /* SOFTOSD */
++#ifdef USE_LNBSHARE
++ VerboseLNBlog = 0;
++ for (int i = 0; i < MAXDEVICES; i++) CardUsesLNBnr[i] = i + 1;
++#endif /* LNBSHARE */
++#ifdef USE_DVLVIDPREFER
++ UseVidPrefer = 0; // default = disabled
++ nVidPrefer = 1;
++ for (int zz = 1; zz < DVLVIDPREFER_MAX; zz++) {
++ VidPreferPrio[ zz ] = 50;
++ VidPreferSize[ zz ] = 100;
++ }
++ VidPreferSize[ 0 ] = 800;
++ VidPreferPrio[ 0 ] = 50;
++#endif /* DVLVIDPREFER */
++#ifdef USE_DVLFRIENDLYFNAMES
++ UseFriendlyFNames = 0; // default = disabled
++#endif /* DVLFRIENDLYFNAMES */
++}
++
++#if defined (USE_DVBSETUP) || defined (USE_NOEPG)
++cSetup::~cSetup()
++{
++#ifdef USE_DVBSETUP
++ free(ChannelBlockerList);
++#endif /* DVBSETUP */
++#ifdef USE_NOEPG
++ free(noEPGList);
++#endif /* NOEPG */
+ }
++#endif /* DVBSETUP + NOEPG */
+
+ cSetup& cSetup::operator= (const cSetup &s)
+ {
+ memcpy(&__BeginData__, &s.__BeginData__, (char *)&s.__EndData__ - (char *)&s.__BeginData__);
++#ifdef USE_DVBSETUP
++ free(ChannelBlockerList);
++ ChannelBlockerList = strdup(s.ChannelBlockerList);
++#endif /* DVBSETUP */
++#ifdef USE_NOEPG
++ free(noEPGList);
++ noEPGList = strdup(s.noEPGList);
++#endif /* NOEPG */
+ return *this;
+ }
+
+@@ -384,11 +572,62 @@ bool cSetup::ParseLanguages(const char *
+ return true;
+ }
+
++#ifdef USE_SOURCECAPS
++void cSetup::StoreSourceCaps(const char *Name)
++{
++ cSetupLine *l;
++ while ((l = Get(Name)) != NULL)
++ Del(l);
++
++ for (int i = 0; i < MAXDEVICES; i++) {
++ char buffer[MAXSOURCECAPS*8]={0,}, *q = buffer;
++ int j = 0;
++ while (SourceCaps[i][j] && j < MAXSOURCECAPS) {
++ if (j==0)
++ q += snprintf(buffer, sizeof(buffer), "%i ", i+1);
++ q += snprintf(q, sizeof(buffer) - (q-buffer), "%s ", *cSource::ToString(SourceCaps[i][j++]));
++ }
++ if (*buffer)
++ Store(Name, buffer, NULL, true);
++ }
++}
++
++bool cSetup::ParseSourceCaps(const char *Value)
++{
++ char *p;
++ int d = strtol(Value, &p, 10)-1, i = 0;
++ while (p < Value+strlen(Value)) {
++ if (*p==0) return true;
++ if (isblank(*p)) ++p;
++ if (isalpha(*p)) {
++ int source = cSource::FromString(p);
++ if (source != cSource::stNone) {
++ SourceCaps[d][i++] = source;
++ SourceCapsSet = true;
++ }
++ else
++ return false;
++ while (!isblank(*p) && *p)
++ ++p;
++ if (i>MAXSOURCECAPS)
++ return false;
++ }
++ }
++ return true;
++}
++#endif /* SOURCECAPS */
++
+ bool cSetup::Parse(const char *Name, const char *Value)
+ {
+ if (!strcasecmp(Name, "OSDLanguage")) { strn0cpy(OSDLanguage, Value, sizeof(OSDLanguage)); I18nSetLocale(OSDLanguage); }
+ else if (!strcasecmp(Name, "OSDSkin")) Utf8Strn0Cpy(OSDSkin, Value, MaxSkinName);
+ else if (!strcasecmp(Name, "OSDTheme")) Utf8Strn0Cpy(OSDTheme, Value, MaxThemeName);
++#ifdef USE_WAREAGLEICON
++ else if (!strcasecmp(Name, "WarEagleIcons")) WarEagleIcons = atoi(Value);
++#endif /* WAREAGLEICON */
++#ifdef USE_VALIDINPUT
++ else if (!strcasecmp(Name, "ShowValidInput")) ShowValidInput = atoi(Value);
++#endif /* VALIDINPUT */
+ else if (!strcasecmp(Name, "PrimaryDVB")) PrimaryDVB = atoi(Value);
+ else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value);
+ else if (!strcasecmp(Name, "TimeoutRequChInfo")) TimeoutRequChInfo = atoi(Value);
+@@ -433,6 +672,21 @@ bool cSetup::Parse(const char *Name, con
+ else if (!strcasecmp(Name, "VideoFormat")) VideoFormat = atoi(Value);
+ else if (!strcasecmp(Name, "UpdateChannels")) UpdateChannels = atoi(Value);
+ else if (!strcasecmp(Name, "UseDolbyDigital")) UseDolbyDigital = atoi(Value);
++#ifdef USE_DOLBYINREC
++ else if (!strcasecmp(Name, "UseDolbyInRecordings")) UseDolbyInRecordings = atoi(Value);
++#endif /* DOLBYINREC */
++#ifdef USE_DVBSETUP
++ else if (!strcasecmp(Name, "DolbyTransferFix")) DolbyTransferFix = atoi(Value);
++ else if (!strcasecmp(Name, "ChannelBlocker")) ChannelBlocker = atoi(Value);
++ else if (!strcasecmp(Name, "ChannelBlockerMode")) ChannelBlockerMode = atoi(Value);
++ else if (!strcasecmp(Name, "ChannelBlockerList")) {
++ free(ChannelBlockerList);
++ ChannelBlockerList = strdup(Value ? Value : "");
++ }
++#endif /* DVBSETUP */
++#ifdef USE_SYNCEARLY
++ else if (!strcasecmp(Name, "UseSyncEarlyPatch")) UseSyncEarlyPatch = atoi(Value);
++#endif /* SYNCEARLY */
+ else if (!strcasecmp(Name, "ChannelInfoPos")) ChannelInfoPos = atoi(Value);
+ else if (!strcasecmp(Name, "ChannelInfoTime")) ChannelInfoTime = atoi(Value);
+ else if (!strcasecmp(Name, "OSDLeft")) OSDLeft = atoi(Value);
+@@ -449,21 +703,152 @@ bool cSetup::Parse(const char *Name, con
+ else if (!strcasecmp(Name, "FontSmlSize")) FontSmlSize = atoi(Value);
+ else if (!strcasecmp(Name, "FontFixSize")) FontFixSize = atoi(Value);
+ else if (!strcasecmp(Name, "MaxVideoFileSize")) MaxVideoFileSize = atoi(Value);
++#ifdef USE_HARDLINKCUTTER
++ else if (!strcasecmp(Name, "MaxRecordingSize")) MaxRecordingSize = atoi(Value);
++#endif /* HARDLINKCUTTER */
+ else if (!strcasecmp(Name, "SplitEditedFiles")) SplitEditedFiles = atoi(Value);
++#ifdef USE_HARDLINKCUTTER
++ else if (!strcasecmp(Name, "HardLinkCutter")) HardLinkCutter = atoi(Value);
++#endif /* HARDLINKCUTTER */
++#ifdef USE_DELTIMESHIFTREC
++ else if (!strcasecmp(Name, "DelTimeshiftRec")) DelTimeshiftRec = atoi(Value);
++#endif /* DELTIMESHIFTREC */
+ else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value);
+ else if (!strcasecmp(Name, "MinUserInactivity")) MinUserInactivity = atoi(Value);
+ else if (!strcasecmp(Name, "NextWakeupTime")) NextWakeupTime = atoi(Value);
+ else if (!strcasecmp(Name, "MultiSpeedMode")) MultiSpeedMode = atoi(Value);
+ else if (!strcasecmp(Name, "ShowReplayMode")) ShowReplayMode = atoi(Value);
++#ifdef USE_DDEPGENTRY
++ else if (!strcasecmp(Name, "DoubleEpgTimeDelta")) DoubleEpgTimeDelta = atoi(Value);
++ else if (!strcasecmp(Name, "DoubleEpgAction")) DoubleEpgAction = atoi(Value);
++ else if (!strcasecmp(Name, "MixEpgAction")) MixEpgAction = atoi(Value);
++ else if (!strcasecmp(Name, "DisableVPS")) DisableVPS = atoi(Value);
++#endif /* DDEPGENTRY */
+ else if (!strcasecmp(Name, "ResumeID")) ResumeID = atoi(Value);
++#ifdef USE_JUMPPLAY
++ else if (!strcasecmp(Name, "JumpPlay")) JumpPlay = atoi(Value);
++ else if (!strcasecmp(Name, "PlayJump")) PlayJump = atoi(Value);
++ else if (!strcasecmp(Name, "PauseLastMark")) PauseLastMark = atoi(Value);
++ else if (!strcasecmp(Name, "ReloadMarks")) ReloadMarks = atoi(Value);
++#endif /* JUMPPLAY */
++#ifdef USE_SOURCECAPS
++ else if (!strcasecmp(Name, "SourceCaps")) return ParseSourceCaps(Value);
++#endif /* SOURCECAPS */
+ else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
+ else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value);
+ else if (!strcasecmp(Name, "CurrentDolby")) CurrentDolby = atoi(Value);
+ else if (!strcasecmp(Name, "InitialChannel")) InitialChannel = atoi(Value);
+ else if (!strcasecmp(Name, "InitialVolume")) InitialVolume = atoi(Value);
++#ifdef USE_VOLCTRL
++ else if (!strcasecmp(Name, "LRVolumeControl")) LRVolumeControl = atoi(Value);
++ else if (!strcasecmp(Name, "LRChannelGroups")) LRChannelGroups = atoi(Value);
++ else if (!strcasecmp(Name, "LRForwardRewind")) LRForwardRewind = atoi(Value);
++#endif /* VOLCTRL */
+ else if (!strcasecmp(Name, "EmergencyExit")) EmergencyExit = atoi(Value);
++#ifdef USE_NOEPG
++ else if (!strcasecmp(Name, "noEPGMode")) noEPGMode = atoi(Value);
++ else if (!strcasecmp(Name, "noEPGList")) {
++ free(noEPGList);
++ noEPGList = strdup(Value ? Value : "");
++ }
++#endif /* NOEPG */
++#ifdef USE_LIRCSETTINGS
++ else if (!strcasecmp(Name, "LircRepeatDelay")) LircRepeatDelay = atoi(Value);
++ else if (!strcasecmp(Name, "LircRepeatFreq")) LircRepeatFreq = atoi(Value);
++ else if (!strcasecmp(Name, "LircRepeatTimeout")) LircRepeatTimeout = atoi(Value);
++#endif /* LIRCSETTINGS */
++#ifdef USE_LIEMIEXT
++ else if (!strcasecmp(Name, "ShowRecDate")) ShowRecDate = atoi(Value);
++ else if (!strcasecmp(Name, "ShowRecTime")) ShowRecTime = atoi(Value);
++ else if (!strcasecmp(Name, "ShowRecLength")) ShowRecLength = atoi(Value);
++ else if (!strcasecmp(Name, "ShowProgressBar")) ShowProgressBar = atoi(Value);
++ else if (!strcasecmp(Name, "MenuCmdPosition")) MenuCmdPosition = atoi(Value);
++ else if (!strcasecmp(Name, "JumpSeconds")) JumpSeconds = atoi(Value);
++ else if (!strcasecmp(Name, "JumpSecondsSlow")) JumpSecondsSlow = atoi(Value);
++ else if (!strcasecmp(Name, "ShowTimerStop")) ShowTimerStop = atoi(Value);
++ else if (!strcasecmp(Name, "MainMenuTitle")) MainMenuTitle = atoi(Value);
++ else if (!strcasecmp(Name, "CustomMainMenuTitle")) Utf8Strn0Cpy(CustomMainMenuTitle, Value, MaxTitleName);
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++ else if (!strcasecmp(Name, "RecordingsSortMode")) RecordingsSortMode = atoi(Value);
++ else if (!strcasecmp(Name, "RecordingsSortDirsFirst")) RecordingsSortDirsFirst = atoi(Value);
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ else if (!strcasecmp(Name, "CutterAutoDelete")) CutterAutoDelete = atoi(Value);
++#endif /* CUTTERQUEUE */
++#ifdef USE_CUTTIME
++ else if (!strcasecmp(Name, "CutTime")) CutTime = atoi(Value);
++#endif /* CUTTIME */
++#ifdef USE_LIVEBUFFER
++ else if (!strcasecmp(Name, "LiveBuffer")) LiveBuffer = atoi(Value);
++ else if (!strcasecmp(Name, "KeepPaused")) KeepPaused = atoi(Value);
++ else if (!strcasecmp(Name, "LiveBufferSize")) LiveBufferSize = atoi(Value);
++ else if (!strcasecmp(Name, "InRAM")) InRAM = atoi(Value);
++ else if (!strcasecmp(Name, "MemBufSize")) MemBufSize = atoi(Value);
++ else if (!strcasecmp(Name, "ExtendBuffer")) ExtendBuffer = atoi(Value);
++#endif /* LIVEBUFFER */
++#ifdef USE_DVDARCHIVE
++ else if (!strcasecmp(Name, "DvdDisplayMode")) DvdDisplayMode = atoi(Value);
++ else if (!strcasecmp(Name, "DvdDisplayZeros")) DvdDisplayZeros = atoi(Value);
++ else if (!strcasecmp(Name, "DvdTrayMode")) DvdTrayMode = atoi(Value);
++ else if (!strcasecmp(Name, "DvdSpeedLimit")) DvdSpeedLimit = atoi(Value);
++#endif /* DVDARCHIVE */
++#ifdef USE_SOFTOSD
++ else if (!strcasecmp(Name, "UseSoftOsd")) UseSoftOsd = atoi(Value);
++ else if (!strcasecmp(Name, "SoftOsdRate")) SoftOsdRate = atoi(Value);
++ else if (!strcasecmp(Name, "SoftOsdFadeinSteps")) SoftOsdFadeinSteps = atoi(Value);
++ else if (!strcasecmp(Name, "SoftOsdFadeoutSteps")) SoftOsdFadeoutSteps = atoi(Value);
++ else if (!strcasecmp(Name, "SoftOsdPaletteOnly")) SoftOsdPaletteOnly = atoi(Value);
++#endif /* SOFTOSD */
++#ifdef USE_DVLVIDPREFER
++ else if (strcasecmp(Name, "UseVidPrefer") == 0) UseVidPrefer = atoi(Value);
++ else if (strcasecmp(Name, "nVidPrefer") == 0) nVidPrefer = atoi(Value);
++ else if (strstr(Name, "VidPrefer") == Name) {
++ char *x = (char *)&Name[ strlen(Name) - 1 ];
++ int vN;
++
++ if (isdigit(*x) != 0) {
++ while (isdigit(*x) != 0)
++ x--;
++ x++;
++ }
++
++ vN = atoi(x);
++ if (vN < DVLVIDPREFER_MAX) {
++ if (strstr(Name, "VidPreferPrio") == Name) {
++ VidPreferPrio[ vN ] = atoi(Value);
++ if (VidPreferPrio[ vN ] > 99)
++ VidPreferPrio[ vN ] = 99;
++ }
++ else if (strstr(Name, "VidPreferSize") == Name) {
++ VidPreferSize[ vN ] = atoi(Value);
++ }
++ else
++ return false;
++ }
++ }
++#endif /* DVLVIDPREFER */
++#ifdef USE_DVLFRIENDLYFNAMES
++ else if (strcasecmp(Name, "UseFriendlyFNames") == 0) UseFriendlyFNames = atoi(Value);
++#endif /* DVLFRIENDLYFNAMES */
+ else
++#ifdef USE_LNBSHARE
++ if (!strcasecmp(Name, "VerboseLNBlog")) VerboseLNBlog = atoi(Value);
++ else {
++ char tmp[20];
++ bool result = false;
++ for (int i = 1; i <= MAXDEVICES; i++) {
++ sprintf(tmp, "Card%dusesLNBnr", i);
++ if (!strcasecmp(Name, tmp)) {
++ CardUsesLNBnr[i - 1] = atoi(Value);
++ result = true;
++ }
++ }
++ return result;
++ }
++#else
+ return false;
++#endif /* LNBSHARE */
+ return true;
+ }
+
+@@ -472,6 +857,12 @@ bool cSetup::Save(void)
+ Store("OSDLanguage", OSDLanguage);
+ Store("OSDSkin", OSDSkin);
+ Store("OSDTheme", OSDTheme);
++#ifdef USE_WAREAGLEICON
++ Store("WarEagleIcons", WarEagleIcons);
++#endif /* WAREAGLEICON */
++#ifdef USE_VALIDINPUT
++ Store("ShowValidInput", ShowValidInput);
++#endif /* VALIDINPUT */
+ Store("PrimaryDVB", PrimaryDVB);
+ Store("ShowInfoOnChSwitch", ShowInfoOnChSwitch);
+ Store("TimeoutRequChInfo", TimeoutRequChInfo);
+@@ -516,6 +907,18 @@ bool cSetup::Save(void)
+ Store("VideoFormat", VideoFormat);
+ Store("UpdateChannels", UpdateChannels);
+ Store("UseDolbyDigital", UseDolbyDigital);
++#ifdef USE_DOLBYINREC
++ Store("UseDolbyInRecordings", UseDolbyInRecordings);
++#endif /* DOLBYINREC */
++#ifdef USE_DVBSETUP
++ Store("DolbyTransferFix", DolbyTransferFix);
++ Store("ChannelBlocker", ChannelBlocker);
++ Store("ChannelBlockerMode", ChannelBlockerMode);
++ Store("ChannelBlockerList", ChannelBlockerList);
++#endif /* DVBSETUP */
++#ifdef USE_SYNCEARLY
++ Store("UseSyncEarlyPatch", UseSyncEarlyPatch);
++#endif /* SYNCEARLY */
+ Store("ChannelInfoPos", ChannelInfoPos);
+ Store("ChannelInfoTime", ChannelInfoTime);
+ Store("OSDLeft", OSDLeft);
+@@ -532,25 +935,164 @@ bool cSetup::Save(void)
+ Store("FontSmlSize", FontSmlSize);
+ Store("FontFixSize", FontFixSize);
+ Store("MaxVideoFileSize", MaxVideoFileSize);
++#ifdef USE_HARDLINKCUTTER
++ Store("MaxRecordingSize", MaxRecordingSize);
++#endif /* HARDLINKCUTTER */
+ Store("SplitEditedFiles", SplitEditedFiles);
++#ifdef USE_HARDLINKCUTTER
++ Store("HardLinkCutter", HardLinkCutter);
++#endif /* HARDLINKCUTTER */
++#ifdef USE_DELTIMESHIFTREC
++ Store("DelTimeshiftRec", DelTimeshiftRec);
++#endif /* DELTIMESHIFTREC */
+ Store("MinEventTimeout", MinEventTimeout);
+ Store("MinUserInactivity", MinUserInactivity);
+ Store("NextWakeupTime", NextWakeupTime);
++#ifdef USE_DDEPGENTRY
++ Store("DoubleEpgAction", DoubleEpgAction);
++ Store("MixEpgAction", MixEpgAction);
++ Store("DisableVPS", DisableVPS);
++ Store("DoubleEpgTimeDelta", DoubleEpgTimeDelta);
++#endif /* DDEPGENTRY */
+ Store("MultiSpeedMode", MultiSpeedMode);
+ Store("ShowReplayMode", ShowReplayMode);
+ Store("ResumeID", ResumeID);
++#ifdef USE_JUMPPLAY
++ Store("JumpPlay", JumpPlay);
++ Store("PlayJump", PlayJump);
++ Store("PauseLastMark", PauseLastMark);
++ Store("ReloadMarks", ReloadMarks);
++#endif /* JUMPPLAY */
++#ifdef USE_SOURCECAPS
++ if (SourceCapsSet) StoreSourceCaps("SourceCaps");
++#endif /* SOURCECAPS */
+ Store("CurrentChannel", CurrentChannel);
+ Store("CurrentVolume", CurrentVolume);
+ Store("CurrentDolby", CurrentDolby);
+ Store("InitialChannel", InitialChannel);
+ Store("InitialVolume", InitialVolume);
++#ifdef USE_VOLCTRL
++ Store("LRVolumeControl", LRVolumeControl);
++ Store("LRChannelGroups", LRChannelGroups);
++ Store("LRForwardRewind", LRForwardRewind);
++#endif /* VOLCTRL */
+ Store("EmergencyExit", EmergencyExit);
++#ifdef USE_NOEPG
++ Store("noEPGMode", noEPGMode);
++ Store("noEPGList", noEPGList);
++#endif /* NOEPG */
++#ifdef USE_LIRCSETTINGS
++ Store("LircRepeatDelay", LircRepeatDelay);
++ Store("LircRepeatFreq", LircRepeatFreq);
++ Store("LircRepeatTimeout", LircRepeatTimeout);
++#endif /* LIRCSETTINGS */
++#ifdef USE_LIEMIEXT
++ Store("ShowRecDate", ShowRecDate);
++ Store("ShowRecTime", ShowRecTime);
++ Store("ShowRecLength", ShowRecLength);
++ Store("ShowProgressBar", ShowProgressBar);
++ Store("MenuCmdPosition", MenuCmdPosition);
++ Store("JumpSeconds", JumpSeconds);
++ Store("JumpSecondsSlow", JumpSecondsSlow);
++ Store("ShowTimerStop", ShowTimerStop);
++ Store("MainMenuTitle", MainMenuTitle);
++ Store("CustomMainMenuTitle",CustomMainMenuTitle);
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++ Store("RecordingsSortMode", RecordingsSortMode);
++ Store("RecordingsSortDirsFirst", RecordingsSortDirsFirst);
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ Store("CutterAutoDelete", CutterAutoDelete);
++#endif /* CUTTERQUEUE */
++#ifdef USE_CUTTIME
++ Store("CutTime", CutTime);
++#endif /* CUTTIME */
++#ifdef USE_LIVEBUFFER
++ Store("LiveBuffer", LiveBuffer);
++ Store("KeepPaused", KeepPaused);
++ Store("LiveBufferSize", LiveBufferSize);
++ Store("InRAM", InRAM);
++ Store("MemBufSize", MemBufSize);
++ Store("ExtendBuffer", ExtendBuffer);
++#endif /* LIVEBUFFER */
++#ifdef USE_DVDARCHIVE
++ Store("DvdDisplayMode", DvdDisplayMode);
++ Store("DvdDisplayZeros", DvdDisplayZeros);
++ Store("DvdTrayMode", DvdTrayMode);
++ Store("DvdSpeedLimit", DvdSpeedLimit);
++#endif /* DVDARCHIVE */
++#ifdef USE_SOFTOSD
++ Store("UseSoftOsd", UseSoftOsd);
++ Store("SoftOsdRate", SoftOsdRate);
++ Store("SoftOsdFadeinSteps", SoftOsdFadeinSteps);
++ Store("SoftOsdFadeoutSteps", SoftOsdFadeoutSteps);
++ Store("SoftOsdPaletteOnly", SoftOsdPaletteOnly);
++#endif /* SOFTOSD */
++#ifdef USE_LNBSHARE
++ Store("VerboseLNBlog", VerboseLNBlog);
++ char tmp[20];
++ if (cDevice::NumDevices() > 1) {
++ for (int i = 1; i <= cDevice::NumDevices(); i++) {
++ sprintf(tmp, "Card%dusesLNBnr", i);
++ Store(tmp, CardUsesLNBnr[i - 1]);
++ }
++ }
++#endif /* LNBSHARE */
++#ifdef USE_DVLVIDPREFER
++ Store ("UseVidPrefer", UseVidPrefer);
++ Store ("nVidPrefer", nVidPrefer);
++
++ char vidBuf[32];
++ for (int zz = 0; zz < nVidPrefer; zz++) {
++ sprintf(vidBuf, "VidPreferPrio%d", zz);
++ Store (vidBuf, VidPreferPrio[zz]);
++ sprintf(vidBuf, "VidPreferSize%d", zz);
++ Store (vidBuf, VidPreferSize[zz]);
++ }
++#endif /* DVLVIDPREFER */
++#ifdef USE_DVLFRIENDLYFNAMES
++ Store ("UseFriendlyFNames", UseFriendlyFNames);
++#endif /* DVLFRIENDLYFNAMES */
+
+ Sort();
+
+ if (cConfig<cSetupLine>::Save()) {
+ isyslog("saved setup to %s", FileName());
++#ifdef USE_DVDARCHIVE
++ if (DvdDisplayMode >= 1) ::Recordings.Load();
++#endif /* DVDARCHIVE */
+ return true;
+ }
+ return false;
+ }
++
++#ifdef USE_CMDRECCMDI18N
++bool LoadCommandsI18n(cCommands & cmds, const char *FileName, bool AllowComments, bool MustExist)
++{
++
++ bool bRet = true;
++ bool bLoadDefault = (bool)strcmp(Setup.OSDLanguage,"en_US");
++ if (bLoadDefault) {
++ // attempt to load a translated file
++ char *FullPath = NULL;
++ asprintf(&FullPath, "%s.%s", FileName, Setup.OSDLanguage);
++ if (!cmds.Load((FullPath), AllowComments, true)) {
++ // require to exist, just to be able to log
++ // fallback
++ bLoadDefault = false;
++ esyslog("Failed to load translated '%s' for language (%s)", FullPath, Setup.OSDLanguage);
++ esyslog("Falling back to default '%s' (if any)", FileName);
++ }
++ free(FullPath);
++ }
++ if (!bLoadDefault) {
++ // let's do it the normal way
++ bRet = cmds.Load(FileName, AllowComments, MustExist);
++ }
++ // return status only for the default commands file
++ return bRet;
++}
++
++#endif /* CMDRECCMDI18N */
++
+diff -ruNp vdr-1.6.0-2/config.h vdr-1.6.0-2-extensions/config.h
+--- vdr-1.6.0-2/config.h 2008-09-11 13:29:43.000000000 +0200
++++ vdr-1.6.0-2-extensions/config.h 2009-04-09 20:48:26.000000000 +0200
+@@ -36,23 +36,74 @@
+ // plugins to work with newer versions of the core VDR as long as no
+ // VDR header files have changed.
+
++#define VDREXTENSIONS 70
++
++#ifdef USE_JUMPPLAY
++#define JUMPPLAYVERSNUM 100
++#endif /* JUMPPLAY */
++
++#ifdef USE_LIEMIEXT
++#define LIEMIKUUTIO 121
++#define MAXMAINMENUTITLE 4
++#define MaxTitleName 64
++#endif /* LIEMIEXT */
++
++#ifdef USE_CMDSUBMENU
++#define CMDSUBMENUVERSNUM 7
++#endif /* CMDSUBMENU */
++
++#ifdef USE_MAINMENUHOOKS
++#define MAINMENUHOOKSVERSNUM 1.0
++#endif /* MAINMENUHOOKS */
++
++#ifdef USE_PINPLUGIN
++#define PIN_PLUGIN_PATCH 120
++#endif /* PINPLUGIN */
++
++#ifdef USE_PLUGINPARAM
++#define PLUGINPARAMPATCHVERSNUM 1
++#endif /* PLUGINPARAM */
++
+ #define MAXPRIORITY 99
+ #define MAXLIFETIME 99
+
++#ifdef USE_DVLVIDPREFER
++#define DVLVIDPREFER_MAX 12
++#endif /* DVLVIDPREFER */
++
+ #define MINOSDWIDTH 480
+ #define MAXOSDWIDTH 672
+ #define MINOSDHEIGHT 324
+ #define MAXOSDHEIGHT 567
+
++#ifdef USE_SOURCECAPS
++#define MAXDEVICES 16 // the maximum number of devices in the system
++#define MAXSOURCECAPS 128 // the maximum number of different sources per device
++#endif /* SOURCECAPS */
++
++#ifdef USE_LNBSHARE
++#ifndef MAXDEVICES
++#define MAXDEVICES 16 // the maximum number of devices in the system
++#endif
++#endif /* LNBSHARE */
++
+ #define MaxFileName 256
+ #define MaxSkinName 16
+ #define MaxThemeName 16
+
++#ifdef USE_CMDSUBMENU
++class cCommands;
++#endif /* CMDSUBMENU */
++
+ class cCommand : public cListObject {
+ private:
+ char *title;
+ char *command;
+ bool confirm;
++#ifdef USE_CMDSUBMENU
++ int nIndent;
++ cCommands *childs;
++#endif /* CMDSUBMENU */
+ static char *result;
+ public:
+ cCommand(void);
+@@ -61,6 +112,14 @@ public:
+ const char *Title(void) { return title; }
+ bool Confirm(void) { return confirm; }
+ const char *Execute(const char *Parameters = NULL);
++#ifdef USE_CMDSUBMENU
++ int getIndent(void) { return nIndent; }
++ void setIndent(int nNewIndent) { nIndent = nNewIndent; }
++ cCommands *getChilds(void) { return childs; }
++ int getChildCount(void);
++ bool hasChilds(void) { return getChildCount() > 0; }
++ void addChild(cCommand *newChild);
++#endif /* CMDSUBMENU */
+ };
+
+ typedef uint32_t in_addr_t; //XXX from /usr/include/netinet/in.h (apparently this is not defined on systems with glibc < 2.2)
+@@ -88,6 +147,9 @@ private:
+ public:
+ cConfig(void) { fileName = NULL; }
+ virtual ~cConfig() { free(fileName); }
++#ifdef USE_CMDSUBMENU
++ virtual void AddConfig(T *Object) { cList<T>::Add(Object); }
++#endif /* CMDSUBMENU */
+ const char *FileName(void) { return fileName; }
+ bool Load(const char *FileName = NULL, bool AllowComments = false, bool MustExist = false)
+ {
+@@ -117,7 +179,11 @@ public:
+ if (!isempty(s)) {
+ T *l = new T;
+ if (l->Parse(s))
++#ifdef USE_CMDSUBMENU
++ AddConfig(l);
++#else
+ Add(l);
++#endif /* CMDSUBMENU */
+ else {
+ esyslog("ERROR: error in %s, line %d", fileName, line);
+ delete l;
+@@ -159,7 +225,14 @@ public:
+ }
+ };
+
++#ifdef USE_CMDSUBMENU
++class cCommands : public cConfig<cCommand> {
++public:
++ virtual void AddConfig(cCommand *Object);
++ };
++#else
+ class cCommands : public cConfig<cCommand> {};
++#endif /* CMDSUBMENU */
+
+ class cSVDRPhosts : public cConfig<cSVDRPhost> {
+ public:
+@@ -168,6 +241,9 @@ public:
+
+ extern cCommands Commands;
+ extern cCommands RecordingCommands;
++#ifdef USE_TIMERCMD
++extern cCommands TimerCommands;
++#endif /* TIMERCMD */
+ extern cSVDRPhosts SVDRPhosts;
+
+ class cSetupLine : public cListObject {
+@@ -193,6 +269,10 @@ private:
+ void StoreLanguages(const char *Name, int *Values);
+ bool ParseLanguages(const char *Value, int *Values);
+ bool Parse(const char *Name, const char *Value);
++#ifdef USE_SOURCECAPS
++ void StoreSourceCaps(const char *Name);
++ bool ParseSourceCaps(const char *Value);
++#endif /* SOURCECAPS */
+ cSetupLine *Get(const char *Name, const char *Plugin = NULL);
+ void Store(const char *Name, const char *Value, const char *Plugin = NULL, bool AllowMultiple = false);
+ void Store(const char *Name, int Value, const char *Plugin = NULL);
+@@ -202,6 +282,12 @@ public:
+ char OSDLanguage[I18N_MAX_LOCALE_LEN];
+ char OSDSkin[MaxSkinName];
+ char OSDTheme[MaxThemeName];
++#ifdef USE_WAREAGLEICON
++ int WarEagleIcons;
++#endif /* WAREAGLEICON */
++#ifdef USE_VALIDINPUT
++ int ShowValidInput;
++#endif /* VALIDINPUT */
+ int PrimaryDVB;
+ int ShowInfoOnChSwitch;
+ int TimeoutRequChInfo;
+@@ -242,6 +328,17 @@ public:
+ int VideoFormat;
+ int UpdateChannels;
+ int UseDolbyDigital;
++#ifdef USE_DOLBYINREC
++ int UseDolbyInRecordings;
++#endif /* DOLBYINREC */
++#ifdef USE_DVBSETUP
++ int DolbyTransferFix;
++ int ChannelBlocker;
++ int ChannelBlockerMode;
++#endif /* DVBSETUP */
++#ifdef USE_SYNCEARLY
++ int UseSyncEarlyPatch;
++#endif /* SYNCEARLY */
+ int ChannelInfoPos;
+ int ChannelInfoTime;
+ int OSDLeft, OSDTop, OSDWidth, OSDHeight;
+@@ -255,20 +352,119 @@ public:
+ int FontSmlSize;
+ int FontFixSize;
+ int MaxVideoFileSize;
++#ifdef USE_HARDLINKCUTTER
++ int MaxRecordingSize;
++#endif /* HARDLINKCUTTER */
+ int SplitEditedFiles;
++#ifdef USE_HARDLINKCUTTER
++ int HardLinkCutter;
++#endif /* HARDLINKCUTTER */
++#ifdef USE_DELTIMESHIFTREC
++ int DelTimeshiftRec;
++#endif /* DELTIMESHIFTREC */
+ int MinEventTimeout, MinUserInactivity;
+ time_t NextWakeupTime;
+ int MultiSpeedMode;
+ int ShowReplayMode;
++#ifdef USE_DDEPGENTRY
++ int DoubleEpgTimeDelta;
++ int DoubleEpgAction;
++ int MixEpgAction;
++ int DisableVPS;
++#endif /* DDEPGENTRY */
+ int ResumeID;
++#ifdef USE_JUMPPLAY
++ int JumpPlay;
++ int PlayJump;
++ int PauseLastMark;
++ int ReloadMarks;
++#endif /* JUMPPLAY */
++#ifdef USE_SOURCECAPS
++ int SourceCaps[MAXDEVICES][MAXSOURCECAPS];
++ bool SourceCapsSet;
++#endif /* SOURCECAPS */
+ int CurrentChannel;
+ int CurrentVolume;
+ int CurrentDolby;
+ int InitialChannel;
+ int InitialVolume;
++#ifdef USE_VOLCTRL
++ int LRVolumeControl;
++ int LRChannelGroups;
++ int LRForwardRewind;
++#endif /* VOLCTRL */
+ int EmergencyExit;
++#ifdef USE_NOEPG
++ int noEPGMode;
++#endif /* NOEPG */
++#ifdef USE_LIRCSETTINGS
++ int LircRepeatDelay;
++ int LircRepeatFreq;
++ int LircRepeatTimeout;
++#endif /* LIRCSETTINGS */
++#ifdef USE_LIEMIEXT
++ int ShowRecDate, ShowRecTime, ShowRecLength, ShowProgressBar, MenuCmdPosition;
++ int JumpSeconds;
++ int JumpSecondsSlow;
++ int ShowTimerStop;
++ int MainMenuTitle;
++ char CustomMainMenuTitle[MaxTitleName];
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++ int RecordingsSortMode;
++ int RecordingsSortDirsFirst;
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ int CutterAutoDelete;
++#endif /* CUTTERQUEUE */
++#ifdef USE_CUTTIME
++ int CutTime;
++#endif /* CUTTIME */
++#ifdef USE_LIVEBUFFER
++ int LiveBuffer;
++ int KeepPaused;
++ int LiveBufferSize;
++ int InRAM;
++ int MemBufSize;
++ int ExtendBuffer;
++#endif /* LIVEBUFFER */
++#ifdef USE_DVDARCHIVE
++ int DvdDisplayMode;
++ int DvdDisplayZeros;
++ int DvdTrayMode;
++ int DvdSpeedLimit;
++#endif /* DVDARCHIVE */
++#ifdef USE_SOFTOSD
++ int UseSoftOsd;
++ int SoftOsdRate;
++ int SoftOsdFadeinSteps;
++ int SoftOsdFadeoutSteps;
++ int SoftOsdPaletteOnly;
++#endif /* SOFTOSD */
++#ifdef USE_LNBSHARE
++ int VerboseLNBlog;
++ int CardUsesLNBnr[MAXDEVICES];
++#endif /* LNBSHARE */
++#ifdef USE_DVLVIDPREFER
++ int UseVidPrefer; // 0 = VDR's default, 1 = use
++ int nVidPrefer;
++ int VidPreferPrio[DVLVIDPREFER_MAX];
++ int VidPreferSize[DVLVIDPREFER_MAX];
++#endif /* DVLVIDPREFER */
++#ifdef USE_DVLFRIENDLYFNAMES
++ int UseFriendlyFNames;
++#endif /* DVLFRIENDLYFNAMES */
+ int __EndData__;
++#ifdef USE_DVBSETUP
++ char *ChannelBlockerList;
++#endif /* DVBSETUP */
++#ifdef USE_NOEPG
++ char *noEPGList; // pointer not to be flat-copied
++#endif /* NOEPG */
+ cSetup(void);
++#if defined (USE_DVBSETUP) || defined (USE_NOEPG)
++ ~cSetup();
++#endif /* DVBSETUP + NOEPG */
+ cSetup& operator= (const cSetup &s);
+ bool Load(const char *FileName);
+ bool Save(void);
+@@ -276,4 +472,8 @@ public:
+
+ extern cSetup Setup;
+
++#ifdef USE_CMDRECCMDI18N
++bool LoadCommandsI18n(cCommands & cmds, const char *FileName = NULL, bool AllowComments = false, bool MustExist = false);
++#endif /* CMDRECCMDI18N */
++
+ #endif //__CONFIG_H
+diff -ruNp vdr-1.6.0-2/cutter.c vdr-1.6.0-2-extensions/cutter.c
+--- vdr-1.6.0-2/cutter.c 2008-01-13 13:22:21.000000000 +0100
++++ vdr-1.6.0-2-extensions/cutter.c 2009-04-09 20:48:26.000000000 +0200
+@@ -15,6 +15,19 @@
+
+ // --- cCuttingThread --------------------------------------------------------
+
++#ifdef USE_CUTTERLIMIT
++#ifndef CUTTER_MAX_BANDWIDTH
++# define CUTTER_MAX_BANDWIDTH MEGABYTE(10) // 10 MB/s
++#endif
++#ifndef CUTTER_REL_BANDWIDTH
++# define CUTTER_REL_BANDWIDTH 75 // %
++#endif
++#ifndef CUTTER_PRIORITY
++# define CUTTER_PRIORITY sched_get_priority_min(SCHED_OTHER)
++#endif
++#define CUTTER_TIMESLICE 100 // ms
++#endif /* CUTTERLIMIT */
++
+ class cCuttingThread : public cThread {
+ private:
+ const char *error;
+@@ -60,6 +73,22 @@ cCuttingThread::~cCuttingThread()
+
+ void cCuttingThread::Action(void)
+ {
++#ifdef USE_CUTTERLIMIT
++#ifdef USE_HARDLINKCUTTER
++ if (!Setup.HardLinkCutter)
++#endif /* HARDLINKCUTTER */
++ {
++ sched_param tmp;
++ tmp.sched_priority = CUTTER_PRIORITY;
++ if (!pthread_setschedparam(pthread_self(), SCHED_OTHER, &tmp))
++ printf("cCuttingThread::Action: cant set priority\n");
++ }
++
++ int bytes = 0;
++ int __attribute__((unused)) burst_size = CUTTER_MAX_BANDWIDTH * CUTTER_TIMESLICE / 1000; // max bytes/timeslice
++ cTimeMs __attribute__((unused)) t;
++#endif /* CUTTERLIMIT */
++
+ cMark *Mark = fromMarks.First();
+ if (Mark) {
+ fromFile = fromFileName->Open();
+@@ -71,6 +100,9 @@ void cCuttingThread::Action(void)
+ Mark = fromMarks.Next(Mark);
+ int FileSize = 0;
+ int CurrentFileNumber = 0;
++#ifdef USE_HARDLINKCUTTER
++ bool SkipThisSourceFile = false;
++#endif /* HARDLINKCUTTER */
+ int LastIFrame = 0;
+ toMarks.Add(0);
+ toMarks.Save();
+@@ -88,12 +120,99 @@ void cCuttingThread::Action(void)
+
+ // Read one frame:
+
++#ifndef USE_HARDLINKCUTTER
+ if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &PictureType, &Length)) {
+ if (FileNumber != CurrentFileNumber) {
+ fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
+ fromFile->SetReadAhead(MEGABYTE(20));
+ CurrentFileNumber = FileNumber;
+ }
++#else
++ if (!fromIndex->Get(Index++, &FileNumber, &FileOffset, &PictureType, &Length)) {
++ error = "index";
++ break;
++ }
++
++ if (FileNumber != CurrentFileNumber) {
++ fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
++ fromFile->SetReadAhead(MEGABYTE(20));
++ CurrentFileNumber = FileNumber;
++ if (SkipThisSourceFile) {
++ // At end of fast forward: Always skip to next file
++ toFile = toFileName->NextFile();
++ if (!toFile) {
++ error = "toFile 4";
++ break;
++ }
++ FileSize = 0;
++ SkipThisSourceFile = false;
++ }
++
++
++ if (Setup.HardLinkCutter && FileOffset == 0) {
++ // We are at the beginning of a new source file.
++ // Do we need to copy the whole file?
++
++ // if !Mark && LastMark, then we're past the last cut-out and continue to next I-frame
++ // if !Mark && !LastMark, then there's just a cut-in, but no cut-out
++ // if Mark, then we're between a cut-in and a cut-out
++
++ uchar MarkFileNumber;
++ int MarkFileOffset;
++ // Get file number of next cut mark
++ if (!Mark && !LastMark
++ || Mark
++ && fromIndex->Get(Mark->position, &MarkFileNumber, &MarkFileOffset)
++ && (MarkFileNumber != CurrentFileNumber)) {
++ // The current source file will be copied completely.
++ // Start new output file unless we did that already
++ if (FileSize != 0) {
++ toFile = toFileName->NextFile();
++ if (!toFile) {
++ error = "toFile 3";
++ break;
++ }
++ FileSize = 0;
++ }
++
++ // Safety check that file has zero size
++ struct stat buf;
++ if (stat(toFileName->Name(), &buf) == 0) {
++ if (buf.st_size != 0) {
++ esyslog("cCuttingThread: File %s exists and has nonzero size", toFileName->Name());
++ error = "nonzero file exist";
++ break;
++ }
++ }
++ else if (errno != ENOENT) {
++ esyslog("cCuttingThread: stat failed on %s", toFileName->Name());
++ error = "stat";
++ break;
++ }
++
++ // Clean the existing 0-byte file
++ toFileName->Close();
++ cString ActualToFileName(ReadLink(toFileName->Name()), true);
++ unlink(ActualToFileName);
++ unlink(toFileName->Name());
++
++ // Try to create a hard link
++ if (HardLinkVideoFile(fromFileName->Name(), toFileName->Name())) {
++ // Success. Skip all data transfer for this file
++ SkipThisSourceFile = true;
++ cutIn = false;
++ toFile = NULL; // was deleted by toFileName->Close()
++ }
++ else {
++ // Fallback: Re-open the file if necessary
++ toFile = toFileName->Open();
++ }
++ }
++ }
++ }
++
++ if (!SkipThisSourceFile) {
++#endif /* HARDLINKCUTTER */
+ if (fromFile) {
+ int len = ReadFrame(fromFile, buffer, Length, sizeof(buffer));
+ if (len < 0) {
+@@ -110,6 +229,7 @@ void cCuttingThread::Action(void)
+ break;
+ }
+ }
++#ifndef USE_HARDLINKCUTTER
+ else {
+ // Error, unless we're past the last cut-in and there's no cut-out
+ if (Mark || LastMark)
+@@ -117,12 +237,17 @@ void cCuttingThread::Action(void)
+ break;
+ }
+
++#endif /* HARDLINKCUTTER */
+ // Write one frame:
+
+ if (PictureType == I_FRAME) { // every file shall start with an I_FRAME
+ if (LastMark) // edited version shall end before next I-frame
+ break;
++#ifndef USE_HARDLINKCUTTER
+ if (FileSize > MEGABYTE(Setup.MaxVideoFileSize)) {
++#else
++ if (!SkipThisSourceFile && FileSize > toFileName->MaxFileSize()) {
++#endif /* HARDLINKCUTTER */
+ toFile = toFileName->NextFile();
+ if (!toFile) {
+ error = "toFile 1";
+@@ -132,12 +257,20 @@ void cCuttingThread::Action(void)
+ }
+ LastIFrame = 0;
+
++#ifndef USE_HARDLINKCUTTER
+ if (cutIn) {
++#else
++ if (!SkipThisSourceFile && cutIn) {
++#endif /* HARDLINKCUTTER */
+ cRemux::SetBrokenLink(buffer, Length);
+ cutIn = false;
+ }
+ }
++#ifndef USE_HARDLINKCUTTER
+ if (toFile->Write(buffer, Length) < 0) {
++#else
++ if (!SkipThisSourceFile && toFile->Write(buffer, Length) < 0) {
++#endif /* HARDLINKCUTTER */
+ error = "safe_write";
+ break;
+ }
+@@ -172,8 +305,44 @@ void cCuttingThread::Action(void)
+ }
+ }
+ else
++#ifndef USE_HARDLINKCUTTER
+ LastMark = true;
++#else
++ LastMark = true; // After last cut-out: Write on until next I-frame, then exit
++#endif /* HARDLINKCUTTER */
++ }
++#ifdef USE_CUTTERLIMIT
++#ifdef USE_HARDLINKCUTTER
++ if (!Setup.HardLinkCutter) {
++#endif /* HARDLINKCUTTER */
++ bytes += Length;
++ if (bytes >= burst_size) {
++ int elapsed = t.Elapsed();
++ int sleep = 0;
++
++#if CUTTER_REL_BANDWIDTH > 0 && CUTTER_REL_BANDWIDTH < 100
++ // stay under max. relative bandwidth
++
++ sleep = (elapsed * 100 / CUTTER_REL_BANDWIDTH) - elapsed;
++ //if (sleep<=0 && elapsed<=2) sleep = 1;
++ //if (sleep) esyslog("cutter: relative bandwidth limit, sleep %d ms (chunk %dk / %dms)", sleep, burst_size/1024, CUTTER_TIMESLICE);
++#endif
++ // stay under max. absolute bandwidth
++ if (elapsed < CUTTER_TIMESLICE) {
++ sleep = max(CUTTER_TIMESLICE - elapsed, sleep);
++ //if (sleep) esyslog("cutter: absolute bandwidth limit, sleep %d ms (chunk %dk / %dms)", sleep, burst_size/1024, CUTTER_TIMESLICE);
++ }
++
++ if (sleep>0)
++ cCondWait::SleepMs(sleep);
++ t.Set();
++ bytes = 0;
++ }
++#ifdef USE_HARDLINKCUTTER
+ }
++#endif /* HARDLINKCUTTER */
++#endif /* CUTTERLIMIT */
++
+ }
+ Recordings.TouchUpdate();
+ }
+@@ -183,18 +352,74 @@ void cCuttingThread::Action(void)
+
+ // --- cCutter ---------------------------------------------------------------
+
++#ifdef USE_CUTTERQUEUE
++#define WAIT_BEFORE_NEXT_CUT (10*1000) // 10 seconds
++
++class cStringListObject : public cListObject {
++ public:
++ cStringListObject(const char *s) { str = strdup(s); }
++ ~cStringListObject() { free(str); }
++
++ const char *Value() { return str; }
++ operator const char * () { return str; }
++
++ private:
++ char *str;
++};
++#endif /* CUTTERQUEUE */
++
+ char *cCutter::editedVersionName = NULL;
+ cCuttingThread *cCutter::cuttingThread = NULL;
+ bool cCutter::error = false;
+ bool cCutter::ended = false;
++#ifdef USE_CUTTERQUEUE
++cMutex *cCutter::cutterLock = new cMutex();
++static uint64_t /*cCutter::*/lastCuttingEndTime = 0;
++static cList<cStringListObject> /**cCutter::*/cutterQueue /*= new cList<cStringListObject>*/;
++#endif /* CUTTERQUEUE */
+
+ bool cCutter::Start(const char *FileName)
+ {
++#ifdef USE_CUTTERQUEUE
++ cMutexLock(cutterLock);
++ if (FileName) {
++ /* Add file to queue.
++ * If cutter is still active, next cutting will be started
++ * when vdr.c:main calls cCutter::Active and previous cutting has
++ * been stopped > 10 s before
++ */
++ cutterQueue.Add(new cStringListObject(FileName));
++ }
++ if (cuttingThread)
++ return true;
++ /* cut next file from queue */
++ if (!(cutterQueue.First()))
++ return false;
++ FileName = cutterQueue.First()->Value();
++#endif /* CUTTERQUEUE */
+ if (!cuttingThread) {
+ error = false;
+ ended = false;
+ cRecording Recording(FileName);
++#ifdef USE_CUTTIME
++ if (Setup.CutTime) {
++ cMarks FromMarks;
++ FromMarks.Load(FileName);
++ cMark *First = FromMarks.First();
++ if (First) Recording.SetStartTime(Recording.start + ((First->position / FRAMESPERSEC + 30) / 60) * 60);
++ }
++#endif /* CUTTIME */
+ const char *evn = Recording.PrefixFileName('%');
++#ifdef USE_CUTTERQUEUE
++ if (!(Recordings.GetByName(FileName))) {
++ // Should _not_ remove any cutted recordings
++ // (original recording already deleted ?)
++ // so, just pop item from queue and return.
++ esyslog("can't cut non-existing recording %s", FileName);
++ cutterQueue.Del(cutterQueue.First());
++ return true; // might be already queued recording
++ }
++#endif /* CUTTERQUEUE */
+ if (evn && RemoveVideoFile(evn) && MakeDirs(evn, true)) {
+ // XXX this can be removed once RenameVideoFile() follows symlinks (see videodir.c)
+ // remove a possible deleted recording with the same name to avoid symlink mixups:
+@@ -220,6 +445,9 @@ bool cCutter::Start(const char *FileName
+
+ void cCutter::Stop(void)
+ {
++#ifdef USE_CUTTERQUEUE
++ cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
+ bool Interrupted = cuttingThread && cuttingThread->Active();
+ const char *Error = cuttingThread ? cuttingThread->Error() : NULL;
+ delete cuttingThread;
+@@ -231,11 +459,20 @@ void cCutter::Stop(void)
+ esyslog("ERROR: '%s' during editing process", Error);
+ RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed?
+ Recordings.DelByName(editedVersionName);
++#ifdef USE_CUTTERQUEUE
++ cutterQueue.Del(cutterQueue.First());
++#endif /* CUTTERQUEUE */
+ }
++#ifdef USE_CUTTERQUEUE
++ lastCuttingEndTime = cTimeMs::Now();
++#endif /* CUTTERQUEUE */
+ }
+
+ bool cCutter::Active(void)
+ {
++#ifdef USE_CUTTERQUEUE
++ cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
+ if (cuttingThread) {
+ if (cuttingThread->Active())
+ return true;
+@@ -246,12 +483,40 @@ bool cCutter::Active(void)
+ free(editedVersionName);
+ editedVersionName = NULL;
+ ended = true;
++#ifdef USE_CUTTERQUEUE
++ if (Setup.CutterAutoDelete) {
++ /* Remove original (if cutting was successful) */
++ if (!error) {
++ cRecording *recording = Recordings.GetByName(*cutterQueue.First());
++ if (!recording)
++ esyslog("ERROR: Can't found '%s' after editing process", cutterQueue.First()->Value());
++ else {
++ if (recording->Delete())
++ Recordings.DelByName(recording->FileName());
++ else
++ esyslog("ERROR: Can't delete '%s' after editing process", cutterQueue.First()->Value());
++ }
++ }
++ lastCuttingEndTime = cTimeMs::Now();
++ }
++ cutterQueue.Del(cutterQueue.First());
++#endif /* CUTTERQUEUE */
++ }
++#ifdef USE_CUTTERQUEUE
++ if (!cuttingThread && cutterQueue.First()) {
++ /* start next cutting from queue*/
++ if (cTimeMs::Now() > lastCuttingEndTime + WAIT_BEFORE_NEXT_CUT)
++ Start(NULL);
+ }
++#endif /* CUTTERQUEUE */
+ return false;
+ }
+
+ bool cCutter::Error(void)
+ {
++#ifdef USE_CUTTERQUEUE
++ cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
+ bool result = error;
+ error = false;
+ return result;
+@@ -259,6 +524,9 @@ bool cCutter::Error(void)
+
+ bool cCutter::Ended(void)
+ {
++#ifdef USE_CUTTERQUEUE
++ cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
+ bool result = ended;
+ ended = false;
+ return result;
+diff -ruNp vdr-1.6.0-2/cutter.h vdr-1.6.0-2-extensions/cutter.h
+--- vdr-1.6.0-2/cutter.h 2002-06-22 12:03:15.000000000 +0200
++++ vdr-1.6.0-2-extensions/cutter.h 2009-04-09 20:48:26.000000000 +0200
+@@ -11,6 +11,9 @@
+ #define __CUTTER_H
+
+ class cCuttingThread;
++#ifdef USE_CUTTERQUEUE
++class cMutex;
++#endif /* CUTTERQUEUE */
+
+ class cCutter {
+ private:
+@@ -18,6 +21,9 @@ private:
+ static cCuttingThread *cuttingThread;
+ static bool error;
+ static bool ended;
++#ifdef USE_CUTTERQUEUE
++ static cMutex *cutterLock;
++#endif /* CUTTERQUEUE */
+ public:
+ static bool Start(const char *FileName);
+ static void Stop(void);
+diff -ruNp vdr-1.6.0-2/device.c vdr-1.6.0-2-extensions/device.c
+--- vdr-1.6.0-2/device.c 2008-09-11 13:29:39.000000000 +0200
++++ vdr-1.6.0-2-extensions/device.c 2009-04-09 20:48:26.000000000 +0200
+@@ -14,11 +14,22 @@
+ #include "audio.h"
+ #include "channels.h"
+ #include "i18n.h"
++#ifdef USE_LIVEBUFFER
++#include "livebuffer.h"
++#endif /* LIVEBUFFER */
+ #include "player.h"
+ #include "receiver.h"
+ #include "status.h"
+ #include "transfer.h"
+
++#ifdef USE_LNBSHARE
++#include "diseqc.h"
++#endif /* LNBSHARE */
++
++#ifdef USE_CHANNELSCAN
++bool scanning_on_receiving_device = false;
++#endif /* CHANNELSCAN */
++
+ // --- cLiveSubtitle ---------------------------------------------------------
+
+ #define LIVESUBTITLEBUFSIZE KILOBYTE(100)
+@@ -228,6 +239,12 @@ cDevice::cDevice(void)
+
+ SetVideoFormat(Setup.VideoFormat);
+
++#ifdef USE_LNBSHARE
++ LNBstate = -1;
++ LNBnr = Setup.CardUsesLNBnr[cardIndex];
++ LNBsource = NULL;
++#endif /* LNBSHARE */
++
+ mute = false;
+ volume = Setup.CurrentVolume;
+
+@@ -253,8 +270,15 @@ cDevice::cDevice(void)
+ for (int i = 0; i < MAXRECEIVERS; i++)
+ receiver[i] = NULL;
+
++#ifdef USE_SOURCECAPS
++ if (numDevices < MAXDEVICES) {
++ device[numDevices++] = this;
++ SetSourceCaps(cardIndex);
++ }
++#else
+ if (numDevices < MAXDEVICES)
+ device[numDevices++] = this;
++#endif /* SOURCECAPS */
+ else
+ esyslog("ERROR: too many devices!");
+ }
+@@ -290,6 +314,16 @@ void cDevice::SetUseDevice(int n)
+ useDevice |= (1 << n);
+ }
+
++#ifdef USE_LNBSHARE
++void cDevice::SetLNBnr(void)
++{
++ for (int i = 0; i < numDevices; i++) {
++ device[i]->LNBnr = Setup.CardUsesLNBnr[i];
++ isyslog("LNB-sharing: setting device %d to use LNB %d", i, device[i]->LNBnr);
++ }
++}
++#endif /* LNBSHARE */
++
+ int cDevice::NextCardIndex(int n)
+ {
+ if (n > 0) {
+@@ -350,6 +384,98 @@ cDevice *cDevice::ActualDevice(void)
+ return d;
+ }
+
++#ifdef USE_LNBSHARE
++cDevice *cDevice::GetBadDevice(const cChannel *Channel)
++{
++ if (!cSource::IsSat(Channel->Source()))
++ return NULL;
++ if (Setup.DiSEqC) {
++ cDiseqc *diseqc;
++ diseqc = Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization());
++
++ for (int i = 0; i < numDevices; i++) {
++ if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBsource() != (int*) diseqc) {
++ if (Setup.VerboseLNBlog)
++ isyslog("LNB %d: Device check for channel %d on device %d. LNB or DiSEq conflict with device %d", LNBnr, Channel->Number(), this->DeviceNumber(), i);
++ return device[i];
++ }
++ }
++ if (Setup.VerboseLNBlog)
++ isyslog("LNB %d: Device check for for channel %d on device %d. OK", LNBnr, Channel->Number(), this->DeviceNumber());
++ }
++ else {
++ char requiredState;
++
++ if (Channel->Frequency() >= Setup.LnbSLOF)
++ requiredState = 1 ;
++ else
++ requiredState = 0;
++
++ if (Channel->Polarization() == 'v' || Channel->Polarization() == 'V')
++ requiredState += 2;
++
++ for (int i = 0; i < numDevices; i++) {
++ if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBconf() != requiredState) {
++ if (Setup.VerboseLNBlog)
++ isyslog("LNB %d: Device check for channel %d, LNBstate %d on device %d, current LNBstate %d. Conflict with device %d, LNBstate %d", LNBnr, Channel->Number(), requiredState, this->DeviceNumber(), LNBstate, i, device[i]->GetLNBconf());
++ return device[i];
++ }
++ }
++ if (Setup.VerboseLNBlog)
++ isyslog("LNB %d: Device check for channel %d, LNBstate %d on device %d, current LNBstate %d. No other devices affected", LNBnr, Channel->Number(), requiredState, this->DeviceNumber(), LNBstate);
++ }
++ return NULL;
++}
++
++int cDevice::GetMaxBadPriority(const cChannel *Channel)
++{
++ if (!cSource::IsSat(Channel->Source())) return -2;
++ bool PrimaryIsBad = false;
++ int maxBadPriority = -2;
++
++ if (Setup.DiSEqC) {
++ cDiseqc *diseqc;
++ diseqc = Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization());
++
++ for (int i = 0; i < numDevices; i++) {
++ if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBsource() != (int*) diseqc) {
++ if (device[i]->Receiving() && device[i]->Priority() > maxBadPriority)
++ maxBadPriority = device[i]->Priority();
++ if (i == ActualDevice()->CardIndex())
++ PrimaryIsBad = true;
++ }
++ }
++ }
++ else {
++ char requiredState;
++ if (Channel->Frequency() >= Setup.LnbSLOF)
++ requiredState = 1 ;
++ else
++ requiredState = 0;
++
++ if (Channel->Polarization() == 'v' || Channel->Polarization() == 'V')
++ requiredState += 2;
++
++ for (int i = 0; i < numDevices; i++) {
++ if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBconf() != requiredState) {
++ if (device[i]->Receiving() && device[i]->Priority() > maxBadPriority)
++ maxBadPriority = device[i]->Priority();
++ if (i == ActualDevice()->CardIndex())
++ PrimaryIsBad = true;
++ }
++ }
++ }
++
++ if (PrimaryIsBad && maxBadPriority == -2)
++ maxBadPriority = -1;
++
++ if (Setup.VerboseLNBlog)
++ isyslog("LNB %d: Request for channel %d on device %d. MaxBadPriority is %d", LNBnr, Channel->Number(), this->DeviceNumber(), maxBadPriority);
++
++ return maxBadPriority;
++}
++#endif /* LNBSHARE */
++
+ cDevice *cDevice::GetDevice(int Index)
+ {
+ return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
+@@ -384,6 +510,10 @@ cDevice *cDevice::GetDevice(const cChann
+ cCamSlot *s = NULL;
+
+ uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
++#ifdef USE_LNBSHARE
++ int badPriority;
++ uint imp2;
++#endif /* LNBSHARE */
+ for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) {
+ if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY)
+ continue; // there is no CAM available in this slot
+@@ -408,6 +538,9 @@ cDevice *cDevice::GetDevice(const cChann
+ imp <<= 1; imp |= LiveView ? !device[i]->IsPrimaryDevice() || ndr : 0; // prefer the primary device for live viewing if we don't need to detach existing receivers
+ imp <<= 1; imp |= !device[i]->Receiving() && (device[i] != cTransferControl::ReceiverDevice() || device[i]->IsPrimaryDevice()) || ndr; // use receiving devices if we don't need to detach existing receivers, but avoid primary device in local transfer mode
+ imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
++#ifdef USE_LIVEBUFFER
++ imp <<= 2; imp |= cLiveBufferManager::Impact(device[i], Channel, LiveView);
++#endif /* LIVEBUFFER */
+ imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
+ imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
+ imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
+@@ -416,7 +549,31 @@ cDevice *cDevice::GetDevice(const cChann
+ imp <<= 1; imp |= NumUsableSlots ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels
+ imp <<= 1; imp |= device[i]->HasDecoder(); // avoid full featured cards
+ imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
++#ifdef USE_LNBSHARE
++ badPriority = device[i]->GetMaxBadPriority(Channel);
++ if (badPriority >= Priority || (badPriority == -1 && Priority < Setup.PrimaryLimit)) {
++ // channel is not available for the requested prioity
++ imp = 0xFFFFFFFF;
++ }
++ else {
++ switch (badPriority) {
++ case -2: // not affected by LNB-sharing
++ imp2 = 0;
++ break;
++ case -1: // the primary device would need a channel switch
++ imp += 1 << 17;
++ imp2 = 0xFFFFFFFF | 1 << 17;
++ break;
++ default: // a device receiving with lower priority would need to be stopped
++ imp += badPriority << 8;
++ imp2 = 0xFFFFFFFF | badPriority << 8;
++ break;
++ }
++ }
++ if (imp < Impact && imp2 < Impact) {
++#else
+ if (imp < Impact) {
++#endif /* LNBSHARE */
+ // This device has less impact than any previous one, so we take it.
+ Impact = imp;
+ d = device[i];
+@@ -457,6 +614,18 @@ void cDevice::SetCamSlot(cCamSlot *CamSl
+ camSlot = CamSlot;
+ }
+
++#ifdef USE_SOURCECAPS
++void cDevice::SetSourceCaps(int Index)
++{
++ for (int d = 0; d < numDevices; d++) {
++ if (Index < 0 || Index == device[d]->CardIndex()) {
++ for (int i = 0; i < MAXSOURCECAPS; i++)
++ device[d]->sourceCaps[i] = Setup.SourceCaps[device[d]->CardIndex()][i];
++ }
++ }
++}
++#endif /* SOURCECAPS */
++
+ void cDevice::Shutdown(void)
+ {
+ primaryDevice = NULL;
+@@ -701,7 +870,11 @@ bool cDevice::ProvidesTransponder(const
+ bool cDevice::ProvidesTransponderExclusively(const cChannel *Channel) const
+ {
+ for (int i = 0; i < numDevices; i++) {
++#ifdef USE_LNBSHARE
++ if (device[i] && device[i] != this && device[i]->ProvidesTransponder(Channel) && device[i]->GetLNBnr() != LNBnr)
++#else
+ if (device[i] && device[i] != this && device[i]->ProvidesTransponder(Channel))
++#endif /* LNBSHARE */
+ return false;
+ }
+ return true;
+@@ -724,6 +897,24 @@ bool cDevice::MaySwitchTransponder(void)
+
+ bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
+ {
++#ifdef USE_LNBSHARE
++ cDevice *tmpDevice;
++ if (this->GetMaxBadPriority(Channel) >= 0) {
++ Skins.Message(mtInfo, tr("Channel locked by LNB!"));
++ return false;
++ }
++ while ((tmpDevice = GetBadDevice(Channel)) != NULL) {
++ if (tmpDevice->IsPrimaryDevice() && LiveView)
++ tmpDevice->SwitchChannelForced(Channel, true);
++ else
++ tmpDevice->SwitchChannelForced(Channel, false);
++ }
++ return SwitchChannelForced(Channel, LiveView);
++}
++
++bool cDevice::SwitchChannelForced(const cChannel *Channel, bool LiveView)
++{
++#endif /* LNBSHARE */
+ if (LiveView) {
+ isyslog("switching to channel %d", Channel->Number());
+ cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
+@@ -753,7 +944,14 @@ bool cDevice::SwitchChannel(int Directio
+ cChannel *channel;
+ while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
+ // try only channels which are currently available
++#ifdef USE_PINPLUGIN
++ if (cStatus::MsgChannelProtected(0, channel) == false)
++#endif /* PINPLUGIN */
++#ifdef USE_LNBSHARE
++ if (PrimaryDevice()->GetMaxBadPriority(channel) < 0 && (GetDevice(channel, 0, true)))
++#else
+ if (GetDevice(channel, 0, true))
++#endif /* LNBSHARE */
+ break;
+ n = channel->Number() + Direction;
+ }
+@@ -774,7 +972,18 @@ bool cDevice::SwitchChannel(int Directio
+
+ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ {
++#ifdef USE_PINPLUGIN
++ // I hope 'LiveView = false' indicates a channel switch for recording,
++ // I really don't know, but it works ...
++ if (LiveView && cStatus::MsgChannelProtected(this, Channel) == true)
++ return scrNotAvailable;
++#endif /* PINPLUGIN */
++
++#ifdef USE_LIVEBUFFER
++ if (LiveView && !(Setup.LiveBuffer && dynamic_cast<cLivePlayer *>(player) != NULL)) {
++#else
+ if (LiveView) {
++#endif /* LIVEBUFFER */
+ StopReplay();
+ DELETENULL(liveSubtitle);
+ DELETENULL(dvbSubtitleConverter);
+@@ -784,15 +993,51 @@ eSetChannelResult cDevice::SetChannel(co
+
+ bool NeedsTransferMode = Device != this;
+
++#ifdef USE_LIVEBUFFER
++ if (LiveView && Setup.LiveBuffer)
++ NeedsTransferMode = true;
++#endif /* LIVEBUFFER */
++
+ eSetChannelResult Result = scrOk;
+
++#ifdef USE_LNBSHARE
++ char requiredState;
++ if (Channel->Frequency() >= Setup.LnbSLOF)
++ requiredState = 1;
++ else
++ requiredState = 0;
++
++ if (Channel->Polarization() == 'v' || Channel->Polarization() == 'V')
++ requiredState += 2;
++
++ if (Setup.VerboseLNBlog)
++ isyslog("LNB %d: Switching device %d to channel %d", LNBnr, this->DeviceNumber(), Channel->Number());
++#endif /* LNBSHARE */
++
+ // If this DVB card can't receive this channel, let's see if we can
+ // use the card that actually can receive it and transfer data from there:
+
+ if (NeedsTransferMode) {
+ if (Device && CanReplay()) {
++#ifdef USE_LNBSHARE
++ if (Device->GetLNBnr() == LNBnr) {
++ if (LNBstate != requiredState || (Setup.DiSEqC && LNBsource != (int*) Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization())) ) {
++ if (IsPrimaryDevice())
++ SetChannelDevice(Channel, true);
++ else
++ SetChannelDevice(Channel, false);
++ LNBstate = requiredState;
++ LNBsource = (int*) Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization());
++ }
++ }
++#endif /* LNBSHARE */
+ cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
+ if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
++#ifdef USE_LIVEBUFFER
++ if (Setup.LiveBuffer)
++ cLiveBufferManager::ChannelSwitch(Device,Channel);
++ else
++#endif /* LIVEBUFFER */
+ cControl::Launch(new cTransferControl(Device, Channel->GetChannelID(), Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
+ else
+ Result = scrNoTransfer;
+@@ -808,6 +1053,10 @@ eSetChannelResult cDevice::SetChannel(co
+ sectionHandler->SetStatus(false);
+ sectionHandler->SetChannel(NULL);
+ }
++#ifdef USE_LNBSHARE
++ LNBstate = requiredState;
++ LNBsource = (int*) Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization());
++#endif /* LNBSHARE */
+ // Tell the camSlot about the channel switch and add all PIDs of this
+ // channel to it, for possible later decryption:
+ if (camSlot)
+@@ -840,7 +1089,11 @@ eSetChannelResult cDevice::SetChannel(co
+ }
+ for (int i = 0; i < MAXSPIDS; i++)
+ SetAvailableTrack(ttSubtitle, i, Channel->Spid(i), Channel->Slang(i));
++#ifdef USE_SYNCEARLY
++ if ((Setup.UseSyncEarlyPatch && (!NeedsTransferMode || GetCurrentAudioTrack() == ttNone)) || (!Setup.UseSyncEarlyPatch && !NeedsTransferMode))
++#else
+ if (!NeedsTransferMode)
++#endif /* SYNCEARLY */
+ EnsureAudioTrack(true);
+ EnsureSubtitleTrack();
+ }
+@@ -1100,7 +1353,12 @@ void cDevice::EnsureSubtitleTrack(void)
+ int LanguagePreference = INT_MAX; // higher than the maximum possible value
+ for (int i = ttSubtitleFirst; i <= ttSubtitleLast; i++) {
+ const tTrackId *TrackId = GetTrack(eTrackType(i));
++#ifdef USE_LIEMIEXT
++ if (TrackId && TrackId->id && (I18nIsPreferredLanguage(Setup.SubtitleLanguages, TrackId->language, LanguagePreference) ||
++ ((i == ttSubtitleFirst + 8) && !(*TrackId->language) && (LanguagePreference == INT_MAX))))
++#else
+ if (TrackId && TrackId->id && I18nIsPreferredLanguage(Setup.SubtitleLanguages, TrackId->language, LanguagePreference))
++#endif /* LIEMIEXT */
+ PreferredTrack = eTrackType(i);
+ }
+ // Make sure we're set to an available subtitle track:
+@@ -1164,7 +1422,11 @@ bool cDevice::Replaying(void) const
+
+ bool cDevice::Transferring(void) const
+ {
++#ifdef USE_LIVEBUFFER
++ return ActualDevice() != PrimaryDevice() || dynamic_cast<cLivePlayer *>(player) != NULL; // TODO
++#else
+ return ActualDevice() != PrimaryDevice();
++#endif /* LIVEBUFFER */
+ }
+
+ bool cDevice::AttachPlayer(cPlayer *Player)
+@@ -1289,7 +1551,11 @@ pre_1_3_19_PrivateStreamDetected:
+ w = PlaySubtitle(Start, d);
+ break;
+ case 0x80: // AC3 & DTS
++#ifdef USE_DOLBYINREC
++ if (Setup.UseDolbyInRecordings) {
++#else
+ if (Setup.UseDolbyDigital) {
++#endif /* DOLBYINREC */
+ SetAvailableTrack(ttDolby, SubStreamIndex, SubStreamId);
+ if ((!VideoOnly || HasIBPTrickSpeed()) && SubStreamId == availableTracks[currentAudioTrack].id) {
+ w = PlayAudio(Start, d, SubStreamId);
+diff -ruNp vdr-1.6.0-2/device.h vdr-1.6.0-2-extensions/device.h
+--- vdr-1.6.0-2/device.h 2008-02-23 14:13:04.000000000 +0100
++++ vdr-1.6.0-2-extensions/device.h 2009-04-09 20:48:26.000000000 +0200
+@@ -23,8 +23,13 @@
+ #include "spu.h"
+ #include "thread.h"
+ #include "tools.h"
++#ifdef USE_ROTOR
++#include <linux/dvb/frontend.h>
++#endif /* ROTOR */
+
++#ifndef USE_SOURCECAPS
+ #define MAXDEVICES 16 // the maximum number of devices in the system
++#endif /* SOURCECAPS */
+ #define MAXPIDHANDLES 64 // the maximum number of different PIDs per device
+ #define MAXRECEIVERS 16 // the maximum number of receivers per device
+ #define MAXVOLUME 255
+@@ -34,6 +39,10 @@
+ #define TS_SYNC_BYTE 0x47
+ #define PID_MASK_HI 0x1F
+
++#ifdef USE_CHANNELSCAN
++extern bool scanning_on_receiving_device;
++#endif /* CHANNELSCAN */
++
+ enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
+
+ enum ePlayMode { pmNone, // audio/video from decoder
+@@ -146,6 +155,35 @@ public:
+ ///< this device/CAM combination will be skipped in the next call to
+ ///< GetDevice().
+ ///< See also ProvidesChannel().
++#ifdef USE_SOURCECAPS
++ static void SetSourceCaps(int Index = -1);
++ ///< Sets the SourceCaps of the given device according to the Setup data.
++#endif /* SOURCECAPS */
++#ifdef USE_LNBSHARE
++private:
++ char LNBstate; // Current frequency band and polarization of the DVB-tuner
++// cDiseqc *LNBsource; // can not #include "diseqc.h". A workaround follows:
++ int *LNBsource; // [DiSEqC] DiSEqC-Source
++ int LNBnr; // Number of LNB used
++public:
++ char GetLNBconf(void) { return LNBstate; }
++ int *GetLNBsource(void) { return LNBsource; }
++ int GetLNBnr(void) { return LNBnr; }
++ static void SetLNBnr(void);
++ cDevice *GetBadDevice(const cChannel *Channel);
++ ///< Returns NULL if there is no device which uses the same LNB or if
++ ///< all of those devices are tuned to the same frequency band and
++ ///< polarization as of the requested channel.
++ ///< Otherwise returns the first device found.
++ int GetMaxBadPriority(const cChannel *Channel);
++ ///< Returns the highest priority of all receiving devices which use
++ ///< the same LNB and are tuned to a different frequency band or
++ ///< polarization as of the requested channel.
++ ///< Returns -1 if there are no such devices, but the primary device
++ ///< would be affected by switching to the requested channel.
++ ///< Returns -2 if there are no such devices and the primary device
++ ///< would not be affected by switching to the requested channel.
++#endif /* LNBSHARE */
+ static void SetAvoidDevice(cDevice *Device) { avoidDevice = Device; }
+ ///< Sets the given Device to be temporarily avoided in the next call to
+ ///< GetDevice(const cChannel, int, bool).
+@@ -156,6 +194,9 @@ private:
+ static int nextCardIndex;
+ int cardIndex;
+ protected:
++#ifdef USE_SOURCECAPS
++ int sourceCaps[MAXSOURCECAPS];
++#endif /* SOURCECAPS */
+ cDevice(void);
+ virtual ~cDevice();
+ virtual bool Ready(void);
+@@ -237,17 +278,30 @@ public:
+ bool SwitchChannel(const cChannel *Channel, bool LiveView);
+ ///< Switches the device to the given Channel, initiating transfer mode
+ ///< if necessary.
++
++#ifdef USE_LNBSHARE
++ bool SwitchChannelForced(const cChannel *Channel, bool LiveView);
++ ///< Switches the device to the given channel, initiating transfer mode
++ ///< if necessary. Forces the switch without taking care of the LNB configuration.
++#endif /* LNBSHARE */
++
+ static bool SwitchChannel(int Direction);
+ ///< Switches the primary device to the next available channel in the given
+ ///< Direction (only the sign of Direction is evaluated, positive values
+ ///< switch to higher channel numbers).
+ private:
++#ifndef USE_YAEPG
+ eSetChannelResult SetChannel(const cChannel *Channel, bool LiveView);
+ ///< Sets the device to the given channel (general setup).
++#endif /* YAEPG */
+ protected:
+ virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+ ///< Sets the device to the given channel (actual physical setup).
+ public:
++#ifdef USE_YAEPG
++ eSetChannelResult SetChannel(const cChannel *Channel, bool LiveView);
++ ///< Sets the device to the given channel (general setup).
++#endif /* YAEPG */
+ static int CurrentChannel(void) { return primaryDevice ? currentChannel : 0; }
+ ///< Returns the number of the current channel on the primary device.
+ static void SetCurrentChannel(const cChannel *Channel) { currentChannel = Channel ? Channel->Number() : 0; }
+@@ -265,6 +319,9 @@ public:
+ virtual bool HasProgramme(void);
+ ///< Returns true if the device is currently showing any programme to
+ ///< the user, either through replaying or live.
++#ifdef USE_ROTOR
++ virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd) { return false; }
++#endif /* ROTOR */
+
+ // PID handle facilities
+
+diff -ruNp vdr-1.6.0-2/dvbdevice.c vdr-1.6.0-2-extensions/dvbdevice.c
+--- vdr-1.6.0-2/dvbdevice.c 2008-02-09 17:11:44.000000000 +0100
++++ vdr-1.6.0-2-extensions/dvbdevice.c 2009-04-09 20:48:26.000000000 +0200
+@@ -71,6 +71,9 @@ static int DvbOpen(const char *Name, int
+ class cDvbTuner : public cThread {
+ private:
+ enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
++#ifdef USE_ROTOR
++ bool SendDiseqc;
++#endif /* ROTOR */
+ int fd_frontend;
+ int cardIndex;
+ int tuneTimeout;
+@@ -83,6 +86,9 @@ private:
+ cMutex mutex;
+ cCondVar locked;
+ cCondVar newSet;
++#ifdef USE_ROTOR
++ dvb_diseqc_master_cmd diseqc_cmd;
++#endif /* ROTOR */
+ bool GetFrontendStatus(fe_status_t &Status, int TimeoutMs = 0);
+ bool SetFrontend(void);
+ virtual void Action(void);
+@@ -91,12 +97,18 @@ public:
+ virtual ~cDvbTuner();
+ bool IsTunedTo(const cChannel *Channel) const;
+ void Set(const cChannel *Channel, bool Tune);
++#ifdef USE_ROTOR
++ bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd);
++#endif /* ROTOR */
+ bool Locked(int TimeoutMs = 0);
+ };
+
+ cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType)
+ {
+ fd_frontend = Fd_Frontend;
++#ifdef USE_ROTOR
++ SendDiseqc = false;
++#endif /* ROTOR */
+ cardIndex = CardIndex;
+ frontendType = FrontendType;
+ tuneTimeout = 0;
+@@ -145,6 +157,19 @@ bool cDvbTuner::Locked(int TimeoutMs)
+ return tunerStatus >= tsLocked;
+ }
+
++#ifdef USE_ROTOR
++bool cDvbTuner::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
++{
++ cMutexLock MutexLock(&mutex);
++ if (frontendType != FE_QPSK || SendDiseqc)
++ return false;
++ diseqc_cmd = cmd;
++ SendDiseqc = true;
++ newSet.Broadcast();
++ return true;
++}
++#endif /* ROTOR */
++
+ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs)
+ {
+ if (TimeoutMs) {
+@@ -210,6 +235,9 @@ bool cDvbTuner::SetFrontend(void)
+ }
+ }
+ diseqcCommands = diseqc->Commands();
++#ifdef USE_DVBSETUP
++ isyslog("Sent DISEQC command: %s", diseqcCommands);
++#endif /* DVBSETUP */
+ }
+ frequency -= diseqc->Lof();
+ }
+@@ -258,6 +286,20 @@ bool cDvbTuner::SetFrontend(void)
+ lockTimeout = DVBC_LOCK_TIMEOUT;
+ }
+ break;
++#ifdef USE_ATSC
++ case FE_ATSC: { // ATSC
++
++ // Frequency and symbol rate:
++
++ Frontend.frequency = FrequencyToHz(channel.Frequency());
++ Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
++ Frontend.u.vsb.modulation = fe_modulation_t(channel.Modulation());
++
++ tuneTimeout = DVBC_TUNE_TIMEOUT;
++ lockTimeout = DVBC_LOCK_TIMEOUT;
++ }
++ break;
++#endif /* ATSC */
+ case FE_OFDM: { // DVB-T
+
+ // Frequency and OFDM paramaters:
+@@ -297,6 +339,12 @@ void cDvbTuner::Action(void)
+ if (GetFrontendStatus(NewStatus, 10))
+ Status = NewStatus;
+ cMutexLock MutexLock(&mutex);
++#ifdef USE_ROTOR
++ if (SendDiseqc) {
++ CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &diseqc_cmd));
++ SendDiseqc = false;
++ }
++#endif /* ROTOR */
+ switch (tunerStatus) {
+ case tsIdle:
+ break;
+@@ -419,6 +467,11 @@ cDvbDevice::cDvbDevice(int n)
+ if (fd_frontend >= 0) {
+ dvb_frontend_info feinfo;
+ if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) {
++#ifdef USE_DVBSETUP
++ if (Setup.ChannelBlockerMode == 4)
++ frontendType = n == Setup.PrimaryDVB - 1 ? frontendType : feinfo.type;
++ else
++#endif /* DVBSETUP */
+ frontendType = feinfo.type;
+ dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType);
+ }
+@@ -638,6 +691,13 @@ eVideoSystem cDvbDevice::GetVideoSystem(
+
+ bool cDvbDevice::SetAudioBypass(bool On)
+ {
++#ifdef USE_DVBSETUP
++ if (Setup.DolbyTransferFix && On) {
++ cChannel *c=Channels.GetByNumber(cDevice::CurrentChannel());
++ if (c->Ca(0) != 0)
++ return false;
++ }
++#endif /* DVBSETUP */
+ if (setTransferModeForDolbyDigital != 1)
+ return false;
+ return ioctl(fd_audio, AUDIO_SET_BYPASS_MODE, On) == 0;
+@@ -743,23 +803,74 @@ void cDvbDevice::TurnOffLiveMode(bool Li
+ bool cDvbDevice::ProvidesSource(int Source) const
+ {
+ int type = Source & cSource::st_Mask;
++#ifdef USE_SOURCECAPS
++ if (Setup.SourceCapsSet && type == cSource::stSat && frontendType == FE_QPSK) {
++ for (int i = 0; i < MAXSOURCECAPS; i++)
++ if (sourceCaps[i] == Source)
++ return true;
++ return false;
++ }
++ else
++#endif /* SOURCECAPS */
+ return type == cSource::stNone
+ || type == cSource::stCable && frontendType == FE_QAM
++#ifdef USE_ATSC
++ || type == cSource::stCable && frontendType == FE_ATSC
++ || type == cSource::stTerr && frontendType == FE_ATSC
++#endif /* ATSC */
+ || type == cSource::stSat && frontendType == FE_QPSK
+ || type == cSource::stTerr && frontendType == FE_OFDM;
+ }
+
+ bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const
+ {
++#ifdef USE_DVBSETUP
++ if (Setup.ChannelBlocker != 0) {
++ if ((Setup.ChannelBlockerMode == 0) ||
++ (Setup.ChannelBlockerMode == 1 && HasDecoder()) ||
++ (Setup.ChannelBlockerMode == 2 && IsPrimaryDevice()) ||
++ (Setup.ChannelBlockerMode == 3 && IsPrimaryDevice() && HasDecoder())) {
++ if ((Setup.ChannelBlocker == 1 && cSource::IsCable(Channel->Source()) && fe_modulation_t(Channel->Modulation()) == QAM_256) ||
++ (Setup.ChannelBlocker == 2 && cSource::IsCable(Channel->Source())) ||
++ (Setup.ChannelBlocker == 3 && cSource::IsSat(Channel->Source())) ||
++ (Setup.ChannelBlocker == 4 && strstr(::Setup.ChannelBlockerList, Channel->GetChannelID().ToString()) != NULL) || // blacklist
++ (Setup.ChannelBlocker == 5 && strstr(::Setup.ChannelBlockerList, Channel->GetChannelID().ToString()) == NULL) || // whitelist
++ (Setup.ChannelBlocker == 6))
++ return false;
++ }
++ }
++#endif /* DVBSETUP */
++
+ return ProvidesSource(Channel->Source()) && (!cSource::IsSat(Channel->Source()) || !Setup.DiSEqC || Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization()));
+ }
+
+ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
+ {
++#ifdef USE_DVBSETUP
++ if (Setup.ChannelBlocker != 0) {
++ if ((Setup.ChannelBlockerMode == 0) ||
++ (Setup.ChannelBlockerMode == 1 && HasDecoder()) ||
++ (Setup.ChannelBlockerMode == 2 && IsPrimaryDevice()) ||
++ (Setup.ChannelBlockerMode == 3 && IsPrimaryDevice() && HasDecoder())) {
++ if ((Setup.ChannelBlocker == 1 && cSource::IsCable(Channel->Source()) && fe_modulation_t(Channel->Modulation()) == QAM_256) ||
++ (Setup.ChannelBlocker == 2 && cSource::IsCable(Channel->Source())) ||
++ (Setup.ChannelBlocker == 3 && cSource::IsSat(Channel->Source())) ||
++ (Setup.ChannelBlocker == 4 && strstr(::Setup.ChannelBlockerList, Channel->GetChannelID().ToString()) != NULL) || // blacklist
++ (Setup.ChannelBlocker == 5 && strstr(::Setup.ChannelBlockerList, Channel->GetChannelID().ToString()) == NULL) || // whitelist
++ (Setup.ChannelBlocker == 6))
++ return false;
++ }
++ }
++#endif /* DVBSETUP */
+ bool result = false;
+ bool hasPriority = Priority < 0 || Priority > this->Priority();
+ bool needsDetachReceivers = false;
+
++#ifdef USE_ANALOGTV
++ if ((Channel->Ca(0) == 0xA0) || (Channel->Ca(0) == 0xA1) || (Channel->Ca(0) == 0xA2))
++ return false;
++#endif /* ANALOGTV */
++
+ if (ProvidesSource(Channel->Source())) {
+ result = hasPriority;
+ if (Priority >= 0 && Receiving(true)) {
+@@ -815,7 +926,11 @@ bool cDvbDevice::SetChannelDevice(const
+ || pidHandlesVideo // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
+ );
+
++#ifdef USE_LIVEBUFFER
++ bool StartTransferMode = IsPrimaryDevice() && !DoTune && !Setup.LiveBuffer
++#else
+ bool StartTransferMode = IsPrimaryDevice() && !DoTune
++#endif /* LIVEBUFFER */
+ && (LiveView && HasPid(vpid ? vpid : apid) && (!pidHandlesVideo || (!pidHandlesAudio && (dpid ? pidHandles[ptAudio].pid != dpid : true)))// the PID is already set as DMX_PES_OTHER
+ || !LiveView && (pidHandlesVideo || pidHandlesAudio) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
+ );
+@@ -871,6 +986,13 @@ bool cDvbDevice::HasLock(int TimeoutMs)
+ return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false;
+ }
+
++#ifdef USE_ROTOR
++bool cDvbDevice::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
++{
++ return dvbTuner->SendDiseqcCmd(cmd);
++}
++#endif /* ROTOR */
++
+ int cDvbDevice::GetAudioChannelDevice(void)
+ {
+ if (HasDecoder()) {
+diff -ruNp vdr-1.6.0-2/dvbdevice.h vdr-1.6.0-2-extensions/dvbdevice.h
+--- vdr-1.6.0-2/dvbdevice.h 2008-02-08 14:48:31.000000000 +0100
++++ vdr-1.6.0-2-extensions/dvbdevice.h 2009-04-09 20:48:26.000000000 +0200
+@@ -71,6 +71,9 @@ protected:
+ virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+ public:
+ virtual bool HasLock(int TimeoutMs = 0);
++#ifdef USE_ROTOR
++ virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd);
++#endif /* ROTOR */
+
+ // PID handle facilities
+
+diff -ruNp vdr-1.6.0-2/dvbosd.c vdr-1.6.0-2-extensions/dvbosd.c
+--- vdr-1.6.0-2/dvbosd.c 2007-09-16 10:55:54.000000000 +0200
++++ vdr-1.6.0-2-extensions/dvbosd.c 2009-04-09 20:48:26.000000000 +0200
+@@ -20,12 +20,26 @@
+ #define MAXNUMWINDOWS 7 // OSD windows are counted 1...7
+ #define MAXOSDMEMORY 92000 // number of bytes available to the OSD (for unmodified DVB cards)
+
++#ifdef USE_SOFTOSD
++ #define SOFTOSD_MAXSIZE (720*576)
++ #define SOFTOSD_MINSIZE (720*64)
++#endif
++
+ class cDvbOsd : public cOsd {
+ private:
+ int osdDev;
+ int osdMem;
+ bool shown;
+ void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL);
++#ifdef USE_SOFTOSD
++ int GetOsdSize() {
++ int OsdSize = 0;
++ cBitmap *Bitmap;
++ for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++)
++ OsdSize += Bitmap->Width() * Bitmap->Height();
++ return OsdSize;
++ }
++#endif
+ protected:
+ virtual void SetActive(bool On);
+ public:
+@@ -76,6 +90,44 @@ void cDvbOsd::SetActive(bool On)
+ }
+ else if (shown) {
+ cBitmap *Bitmap;
++#ifdef USE_SOFTOSD
++ if (Setup.UseSoftOsd) {
++ if (SOFTOSD_MAXSIZE > GetOsdSize() && SOFTOSD_MINSIZE < GetOsdSize()) {
++ for (int fade = Setup.SoftOsdFadeoutSteps - 1; fade > 0; fade--) {
++ int64_t flush_start = cTimeMs::Now();
++ for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) {
++ Cmd(OSD_SetWindow, 0, i + 1);
++ int NumColors;
++ const tColor *Colors = Bitmap->Colors(NumColors);
++ if (Colors) {
++ tColor colors[NumColors];
++ for (int i = 0; i < NumColors; i++) {
++ // convert AARRGGBB to AABBGGRR (the driver expects the colors the wrong way):
++ int alpha = (Colors[i] >> 24) & 0x000000FF;
++ alpha = (alpha * fade) / Setup.SoftOsdFadeoutSteps;
++ colors[i] = (alpha << 24) | ((Colors[i] & 0x0000FF) << 16) | (Colors[i] & 0x00FF00) | ((Colors[i] & 0xFF0000) >> 16);
++ }
++ Colors = colors;
++ Cmd(OSD_SetPalette, 0, NumColors - 1, 0, 0, 0, Colors);
++ if (!Setup.SoftOsdPaletteOnly) {
++ //Cmd(OSD_SetBlock, Bitmap->Width(), 0, 0, Bitmap->Width() - 1, Bitmap->Height() - 1, Bitmap->Data(0, 0));
++ Cmd(OSD_SetBlock, Bitmap->Width(), 0, 0, 0, 0, Bitmap->Data(0, 0));
++ }
++ }
++ }
++ int flush_time = cTimeMs::Now() - flush_start;
++ dsyslog("SOFTOSD: FadeOut Step %d from %d FlushTime = %d ms",
++ Setup.SoftOsdFadeoutSteps - fade, Setup.SoftOsdFadeoutSteps - 1, flush_time);
++ int wait_time = 1000 / Setup.SoftOsdRate;
++ while (flush_time > wait_time && fade > 2) {
++ fade--;
++ flush_time -= wait_time;
++ }
++ cCondWait::SleepMs(wait_time-flush_time);
++ }
++ }
++ }
++#endif /* SOFTOSD */
+ for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) {
+ Cmd(OSD_SetWindow, 0, i + 1);
+ Cmd(OSD_Close);
+@@ -83,6 +135,12 @@ void cDvbOsd::SetActive(bool On)
+ shown = false;
+ }
+ }
++#ifdef USE_YAEPG
++ if (vidWin.bpp != 0) {
++ Cmd(OSD_SetWindow, 0, 5);
++ Cmd(OSD_Close);
++ }
++#endif /* YAEPG */
+ }
+
+ eOsdError cDvbOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
+@@ -182,6 +240,12 @@ void cDvbOsd::Flush(void)
+ for (int i = 0; i < NumColors; i++) {
+ // convert AARRGGBB to AABBGGRR (the driver expects the colors the wrong way):
+ colors[i] = (Colors[i] & 0xFF000000) | ((Colors[i] & 0x0000FF) << 16) | (Colors[i] & 0x00FF00) | ((Colors[i] & 0xFF0000) >> 16);
++#ifdef USE_SOFTOSD
++ if (Setup.UseSoftOsd && !shown) {
++ if (SOFTOSD_MAXSIZE > GetOsdSize() && SOFTOSD_MINSIZE < GetOsdSize())
++ colors[i] = 0;
++ }
++#endif /* SOFTOSD */
+ }
+ Colors = colors;
+ //TODO end of stuff that should be fixed in the driver
+@@ -198,6 +262,51 @@ void cDvbOsd::Flush(void)
+ Cmd(OSD_SetWindow, 0, i + 1);
+ Cmd(OSD_MoveWindow, 0, Left() + Bitmap->X0(), Top() + Bitmap->Y0());
+ }
++#ifdef USE_YAEPG
++ if (vidWin.bpp != 0) {
++ Cmd(OSD_SetWindow, 0, 5);
++ Cmd(OSD_OpenRaw, vidWin.bpp, vidWin.x1, vidWin.y1, vidWin.x2, vidWin.y2, (void *)0);
++ }
++#endif /* YAEPG */
++#ifdef USE_SOFTOSD
++ if (Setup.UseSoftOsd) {
++ if (SOFTOSD_MAXSIZE > GetOsdSize() && SOFTOSD_MINSIZE < GetOsdSize()) {
++ for (int fade = 1; fade <= Setup.SoftOsdFadeinSteps; fade++) {
++ int64_t flush_start = cTimeMs::Now();
++ for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) {
++ Cmd(OSD_SetWindow, 0, i + 1);
++ int NumColors;
++ const tColor *Colors = Bitmap->Colors(NumColors);
++ if (Colors) {
++ tColor colors[NumColors];
++ for (int i = 0; i < NumColors; i++) {
++ // convert AARRGGBB to AABBGGRR:
++ int alpha = (Colors[i]>>24) & 0x000000FF;
++ alpha = (alpha * fade) / Setup.SoftOsdFadeinSteps;
++ colors[i] = (alpha << 24) | ((Colors[i] & 0x0000FF) << 16) | (Colors[i] & 0x00FF00) | ((Colors[i] & 0xFF0000) >> 16);
++ }
++ Colors = colors;
++ Cmd(OSD_SetPalette, 0, NumColors - 1, 0, 0, 0, Colors);
++ if (!Setup.SoftOsdPaletteOnly) {
++ //Cmd(OSD_SetBlock, Bitmap->Width(), 0, 0, Bitmap->Width() - 1, Bitmap->Height() - 1, Bitmap->Data(0, 0));
++ Cmd(OSD_SetBlock, Bitmap->Width(), 0, 0, 0, 0, Bitmap->Data(0, 0));
++ }
++ }
++ }
++ int flush_time = cTimeMs::Now() - flush_start;
++ dsyslog("SOFTOSD: FadeIn Step %d from %d FlushTime = %d ms", fade, Setup.SoftOsdFadeinSteps, flush_time);
++ int wait_time = 1000 / Setup.SoftOsdRate;
++ while (flush_time > wait_time && fade < Setup.SoftOsdFadeinSteps - 1) {
++ fade++;
++ flush_time -= wait_time;
++ }
++ if (fade == Setup.SoftOsdFadeinSteps) /* last step, don't wait */
++ break;
++ cCondWait::SleepMs(wait_time - flush_time);
++ }
++ }
++ }
++#endif /* SOFTOSD */
+ shown = true;
+ }
+ }
+diff -ruNp vdr-1.6.0-2/dvbplayer.c vdr-1.6.0-2-extensions/dvbplayer.c
+--- vdr-1.6.0-2/dvbplayer.c 2008-02-09 16:10:54.000000000 +0100
++++ vdr-1.6.0-2-extensions/dvbplayer.c 2009-04-09 20:48:26.000000000 +0200
+@@ -14,6 +14,9 @@
+ #include "ringbuffer.h"
+ #include "thread.h"
+ #include "tools.h"
++#ifdef USE_TTXTSUBS
++#include "vdrttxtsubshooks.h"
++#endif /* TTXTSUBS */
+
+ // --- cBackTrace ------------------------------------------------------------
+
+@@ -193,6 +196,9 @@ private:
+ cNonBlockingFileReader *nonBlockingFileReader;
+ cRingBufferFrame *ringBuffer;
+ cBackTrace *backTrace;
++#ifdef USE_JUMPPLAY
++ cMarksReload marks;
++#endif /* JUMPPLAY */
+ cFileName *fileName;
+ cIndexFile *index;
+ cUnbufferedFile *replayFile;
+@@ -234,7 +240,11 @@ public:
+ int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
+
+ cDvbPlayer::cDvbPlayer(const char *FileName)
++#ifdef USE_JUMPPLAY
++:cThread("dvbplayer"), marks(FileName)
++#else
+ :cThread("dvbplayer")
++#endif /* JUMPPLAY */
+ {
+ nonBlockingFileReader = NULL;
+ ringBuffer = NULL;
+@@ -312,6 +322,35 @@ void cDvbPlayer::Empty(void)
+ firstPacket = true;
+ }
+
++
++#ifdef USE_TTXTSUBS
++static void StripExtendedPackets(uchar *b, int Length)
++{
++ for (int i = 0; i < Length - 6; i++) {
++ if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
++ uchar c = b[i + 3];
++ int l = b[i + 4] * 256 + b[i + 5] + 6;
++ switch (c) {
++ case 0xBD: // dolby
++ // EBU Teletext data, ETSI EN 300 472
++ if (b[i + 8] == 0x24 && b[i + 45] >= 0x10 && b[i + 45] < 0x20) {
++ cVDRTtxtsubsHookListener::Hook()->PlayerTeletextData(&b[i], l);
++ // continue with deleting the data - otherwise it disturbs DVB replay
++ int n = l;
++ for (int j = i; j < Length && n--; j++)
++ b[j] = 0x00;
++ }
++ break;
++ default:
++ break;
++ }
++ if (l)
++ i += l - 1; // the loop increments, too!
++ }
++ }
++}
++#endif /* TTXTSUBS */
++
+ bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset)
+ {
+ if (FileNumber > 0)
+@@ -341,6 +380,11 @@ bool cDvbPlayer::Save(void)
+ if (index) {
+ int Index = writeIndex;
+ if (Index >= 0) {
++#ifdef USE_JUMPPLAY
++ // set resume position to 0 if replay stops at the first mark
++ if (Setup.PlayJump && marks.First() && abs(Index - marks.First()->position) <= RESUMEBACKUP)
++ Index = 0;
++#endif /* JUMPPLAY */
+ Index -= RESUMEBACKUP;
+ if (Index > 0)
+ Index = index->GetNextIFrame(Index, false);
+@@ -363,16 +407,154 @@ void cDvbPlayer::Activate(bool On)
+ Cancel(9);
+ }
+
++#ifdef USE_DVBPLAYER
++// --- BEGIN fix for I frames -------------------------------------------
++//
++// Prior to the introduction of cVideoRepacker, VDR didn't start a new
++// PES packet when a new frame started. So, it was likely that the tail
++// of an I frame was at the beginning of the packet which started the
++// following B frame. Due to the organisation of VDR's index file, VDR
++// typically didn't read the tail of the I frame and therefore caused
++// softdevice plugins to not render such a frame as it was incomplete,
++// e. g. when moving cutting marks.
++//
++// The following code tries to fix incomplete I frames for recordings
++// made prior to the introdcution of cVideoRepacker, to be able to
++// edit cutting marks for example with softdevice plugins like vdr-xine.
++//
++
++static uchar *findStartCode(uchar *Data, int Length, int &PesPayloadOffset)
++{
++ uchar *limit = Data + Length;
++ if (AnalyzePesHeader(Data, Length, PesPayloadOffset) <= phInvalid)
++ return 0; // neither MPEG1 nor MPEG2
++
++ Data += PesPayloadOffset + 3; // move to video payload and skip 00 00 01
++ while (Data < limit) {
++ // possible start codes that appear before/after picture data
++ // 00 00 01 B3: sequence header code
++ // 00 00 01 B8: group start code
++ // 00 00 01 00: picture start code
++ // 00 00 01 B7: sequence end code
++ if (0x01 == Data[-1] && (0xB3 == Data[0] || 0xB8 == Data[0] || 0x00 == Data[0] || 0xB7 == Data[0]) && 0x00 == Data[-2] && 0x00 == Data[-3])
++ return Data - 3;
++ Data++;
++ }
++
++ return 0;
++}
++
++static void fixIFrameHead(uchar *Data, int Length)
++{
++ int pesPayloadOffset = 0;
++ uchar *p = findStartCode(Data, Length, pesPayloadOffset);
++ if (!p) {
++ esyslog("fixIframeHead: start code not found!\n");
++ return;
++ }
++
++ Data += pesPayloadOffset; // move to video payload
++ if (Data < p)
++ memset(Data, 0, p - Data); // zero preceeding bytes
++}
++
++static int fixIFrameTail(uchar *Data, int Length)
++{
++ int pesPayloadOffset = 0;
++ uchar *p = findStartCode(Data, Length, pesPayloadOffset);
++ if (!p) {
++ esyslog("fixIframeTail: start code not found!\n");
++ return Length;
++ }
++
++ // is this PES packet required?
++ uchar *videoPayload = Data + pesPayloadOffset;
++ if (videoPayload >= p)
++ return 0; // no
++
++ // adjust PES length
++ int lenPES = (p - Data);
++ Data[4] = (lenPES - 6) >> 8;
++ Data[5] = (lenPES - 6) & 0xFF;
++
++ return lenPES;
++}
++
++#define IPACKS 2048 // originally defined in remux.c
++
++static void fixIFrame(uchar *Data, int &Length, const int OriginalLength)
++{
++ int done = 0;
++
++ while (done < Length) {
++ if (0x00 != Data[0] || 0x00 != Data[1] || 0x01 != Data[2]) {
++ esyslog("fixIFrame: PES start code not found at offset %d (data length: %d, original length: %d)!", done, Length, OriginalLength);
++ if (Length > OriginalLength) // roll back additional data
++ Length = OriginalLength;
++ return;
++ }
++
++ int lenPES = 6 + Data[4] * 256 + Data[5];
++ if (0xBA == Data[3]) { // pack header has fixed length
++ if (0x00 == (0xC0 & Data[4]))
++ lenPES = 12; // MPEG1
++ else
++ lenPES = 14 + (Data[13] & 0x07); // MPEG2
++ }
++ else if (0xB9 == Data[3]) // stream end has fixed length
++ lenPES = 4;
++ else if (0xE0 == (0xF0 & Data[3])) { // video packet
++ int todo = Length - done;
++ int bite = (lenPES < todo) ? lenPES : todo;
++ if (0 == done) // first packet
++ fixIFrameHead(Data, bite);
++ else if (done >= OriginalLength) { // last packet
++ Length = done + fixIFrameTail(Data, bite);
++ return;
++ }
++ }
++ else if (0 == done && 0xC0 == (0xE0 & Data[3])) {
++ // if the first I frame packet is an audio packet then this is a radio recording: don't touch it!
++ if (Length > OriginalLength) // roll back additional data
++ Length = OriginalLength;
++ return;
++ }
++
++ done += lenPES;
++ Data += lenPES;
++ }
++}
++
++// --- END fix for I frames ---------------------------------------------
++#endif /* DVBPLAYER */
++
+ void cDvbPlayer::Action(void)
+ {
+ uchar *b = NULL;
+ uchar *p = NULL;
+ int pc = 0;
++#ifdef USE_JUMPPLAY
++ bool cutIn = false;
++ int total = -1;
++#endif /* JUMPPLAY */
+
+ readIndex = Resume();
+ if (readIndex >= 0)
+ isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true));
+
++#ifdef USE_JUMPPLAY
++ if (Setup.PlayJump && readIndex <= 0 && marks.First() && index) {
++ int Index = marks.First()->position;
++ uchar FileNumber;
++ int FileOffset;
++ if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset)) {
++ isyslog("PlayJump: start replay at first mark %d (%s)", Index, *IndexToHMSF(Index, true));
++ readIndex = Index;
++ }
++ }
++
++ bool LastMarkPause = false;
++#endif /* JUMPPLAY */
+ nonBlockingFileReader = new cNonBlockingFileReader;
+ int Length = 0;
+ bool Sleep = false;
+@@ -393,7 +575,11 @@ void cDvbPlayer::Action(void)
+
+ // Read the next frame from the file:
+
++#ifdef USE_JUMPPLAY
++ if (playMode != pmStill && playMode != pmPause && !LastMarkPause) {
++#else
+ if (playMode != pmStill && playMode != pmPause) {
++#endif /* JUMPPLAY */
+ if (!readFrame && (replayFile || readIndex >= 0)) {
+ if (!nonBlockingFileReader->Reading()) {
+ if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
+@@ -411,6 +597,9 @@ void cDvbPlayer::Action(void)
+ if (!NextFile(FileNumber, FileOffset)) {
+ readIndex = Index;
+ continue;
++#ifdef USE_DVBPLAYER
++ Length += IPACKS; // fixIFrame needs next video packet
++#endif /* DVBPLAYER */
+ }
+ }
+ else {
+@@ -438,6 +627,43 @@ void cDvbPlayer::Action(void)
+ uchar FileNumber;
+ int FileOffset;
+ readIndex++;
++#ifdef USE_JUMPPLAY
++ if (Setup.PlayJump || Setup.PauseLastMark) {
++ // check for end mark - jump to next mark or pause
++ marks.Reload();
++ cMark *m = marks.Get(readIndex);
++ if (m && (m->Index() & 0x01) != 0) {
++ m = marks.Next(m);
++ int Index;
++ if (m)
++ Index = m->position;
++ else if (Setup.PauseLastMark) {
++ // pause at last mark
++ isyslog("PauseLastMark: pause at position %d (%s)", readIndex, *IndexToHMSF(readIndex, true));
++ LastMarkPause = true;
++ Index = -1;
++ }
++ else if (total == index->Last())
++ // at last mark jump to end of recording
++ Index = index->Last() - 1;
++ else
++ // jump but stay off end of live-recordings
++ Index = index->GetNextIFrame(index->Last() - 150, true);
++
++ // don't jump in edited recordings
++ if (Setup.PlayJump && Index > readIndex && Index > index->GetNextIFrame(readIndex, true)) {
++ isyslog("PlayJump: %d frames to %d (%s)", Index - readIndex, Index, *IndexToHMSF(Index, true));
++ readIndex = Index;
++ cutIn = true;
++ }
++ }
++ }
++ // for detecting growing length of live-recordings
++ uchar PictureType;
++ if (index->Get(readIndex, &FileNumber, &FileOffset, &PictureType) &&
++ PictureType == I_FRAME)
++ total = index->Last();
++#endif /* JUMPPLAY */
+ if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) {
+ readIndex = -1;
+ eof = true;
+@@ -457,6 +683,10 @@ void cDvbPlayer::Action(void)
+ int r = nonBlockingFileReader->Read(replayFile, b, Length);
+ if (r > 0) {
+ WaitingForData = false;
++#ifdef USE_DVBPLAYER
++ if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
++ fixIFrame(b, r, Length - IPACKS);
++#endif /* DVBPLAYER */
+ readFrame = new cFrame(b, -r, ftUnknown, readIndex); // hands over b to the ringBuffer
+ b = NULL;
+ }
+@@ -473,6 +703,12 @@ void cDvbPlayer::Action(void)
+ // Store the frame in the buffer:
+
+ if (readFrame) {
++#ifdef USE_JUMPPLAY
++ if (cutIn) {
++ cRemux::SetBrokenLink(readFrame->Data(), readFrame->Count());
++ cutIn = false;
++ }
++#endif /* JUMPPLAY */
+ if (ringBuffer->Put(readFrame))
+ readFrame = NULL;
+ }
+@@ -503,6 +739,9 @@ void cDvbPlayer::Action(void)
+ }
+ }
+ if (p) {
++#ifdef USE_TTXTSUBS
++ StripExtendedPackets(p, pc);
++#endif /* TTXTSUBS */
+ int w = PlayPes(p, pc, playMode != pmPlay);
+ if (w > 0) {
+ p += w;
+@@ -521,8 +760,19 @@ void cDvbPlayer::Action(void)
+ p = NULL;
+ }
+ }
++#ifdef USE_JUMPPLAY
++ else {
++ if (LastMarkPause) {
++ LastMarkPause = false;
++ playMode = pmPause;
++ writeIndex = readIndex;
++ }
++ Sleep = true;
++ }
++#else
+ else
+ Sleep = true;
++#endif /* JUMPPLAY */
+ }
+ }
+
+@@ -699,9 +949,15 @@ void cDvbPlayer::Goto(int Index, bool St
+ int FileOffset, Length;
+ Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
+ if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
++#ifdef USE_DVBPLAYER
++ Length += IPACKS; // fixIFrame needs next video packet
++#endif /* DVBPLAYER */
+ uchar b[MAXFRAMESIZE + 4 + 5 + 4];
+ int r = ReadFrame(replayFile, b, Length, sizeof(b));
+ if (r > 0) {
++#ifdef USE_DVBPLAYER
++ fixIFrame(b, r, Length - IPACKS);
++#endif /* DVBPLAYER */
+ if (playMode == pmPause)
+ DevicePlay();
+ // append sequence end code to get the image shown immediately with softdevices
+diff -ruNp vdr-1.6.0-2/eit.c vdr-1.6.0-2-extensions/eit.c
+--- vdr-1.6.0-2/eit.c 2008-09-11 13:29:39.000000000 +0200
++++ vdr-1.6.0-2-extensions/eit.c 2009-04-09 20:48:26.000000000 +0200
+@@ -17,13 +17,40 @@
+ #include "libsi/section.h"
+ #include "libsi/descriptor.h"
+
++#ifdef USE_SETTIME
++extern char *SetTime;
++#endif /* SETTIME */
++
+ // --- cEIT ------------------------------------------------------------------
+
+ class cEIT : public SI::EIT {
+ public:
+ cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus = false);
++#ifdef USE_NOEPG
++private:
++ bool allowedEPG(tChannelID kanalID);
++#endif /* NOEPG */
+ };
+
++#ifdef USE_NOEPG
++bool cEIT::allowedEPG(tChannelID kanalID) {
++ bool rc;
++
++ if (Setup.noEPGMode == 1) {
++ rc = false;
++ if (strstr(::Setup.noEPGList, kanalID.ToString()) != NULL)
++ rc = true;
++ }
++ else {
++ rc = true;
++ if (strstr(::Setup.noEPGList, kanalID.ToString()) != NULL)
++ rc = false;
++ }
++
++ return rc;
++}
++#endif /* NOEPG */
++
+ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus)
+ :SI::EIT(Data, false)
+ {
+@@ -35,6 +62,14 @@ cEIT::cEIT(cSchedules *Schedules, int So
+ if (!channel)
+ return; // only collect data for known channels
+
++#ifdef USE_NOEPG
++ // only use epg from channels not blocked by noEPG-patch
++ tChannelID kanalID;
++ kanalID = channel->GetChannelID();
++ if (!allowedEPG(kanalID))
++ return;
++#endif /* NOEPG */
++
+ cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
+
+ bool Empty = true;
+@@ -71,8 +106,74 @@ cEIT::cEIT(cSchedules *Schedules, int So
+ // If the existing event has a zero table ID it was defined externally and shall
+ // not be overwritten.
+ if (pEvent->TableID() == 0x00) {
++#ifdef USE_DDEPGENTRY
++ if (pEvent->Version() == getVersionNumber()) {
++ if (Setup.MixEpgAction == 0)
++ continue;
++ //printf("in");
++ //printf("%s", pEvent->GetTimeString());
++ // to use the info of the original epg, update the extern one,
++ // if it has less info
++ SI::Descriptor *d;
++ SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL;
++ //SI::ExtendedEventDescriptor *eed = NULL;
++ SI::ShortEventDescriptor *ShortEventDescriptor = NULL;
++ //SI::ShortEventDescriptor *sed = NULL;
++ //SI::TimeShiftedEventDescriptor *tsed = NULL;
++ //cLinkChannels *LinkChannels = NULL;
++ for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2));) {
++ if (d->getDescriptorTag() == SI::ShortEventDescriptorTag) {
++ int LanguagePreferenceShort = -1;
++ SI::ShortEventDescriptor *sed = (SI::ShortEventDescriptor *)d;
++ if (I18nIsPreferredLanguage(Setup.EPGLanguages, sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
++ delete ShortEventDescriptor;
++ ShortEventDescriptor = sed;
++ d = NULL; // so that it is not deleted
++ }
++ }
++ else if (d->getDescriptorTag() == SI::ExtendedEventDescriptorTag) {
++ int LanguagePreferenceExt = -1;
++ bool UseExtendedEventDescriptor = false;
++ SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d;
++ if (I18nIsPreferredLanguage(Setup.EPGLanguages, eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
++ delete ExtendedEventDescriptors;
++ ExtendedEventDescriptors = new SI::ExtendedEventDescriptors;
++ UseExtendedEventDescriptor = true;
++ }
++ if (UseExtendedEventDescriptor) {
++ ExtendedEventDescriptors->Add(eed);
++ d = NULL; // so that it is not deleted
++ }
++ if (eed->getDescriptorNumber() == eed->getLastDescriptorNumber())
++ UseExtendedEventDescriptor = false;
++ }
++ delete d;
++ }
++ if (pEvent) {
++ if (ShortEventDescriptor) {
++ char buffer[256];
++ if (ShortEventDescriptor->text.getText(buffer, sizeof(buffer)) && pEvent->ShortText() && (strlen(ShortEventDescriptor->text.getText(buffer, sizeof(buffer))) > strlen(pEvent->ShortText()))) {
++ pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer, sizeof(buffer)));
++ pEvent->FixEpgBugs();
++ }
++ }
++ if (ExtendedEventDescriptors) {
++ char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ") + 1];
++ //pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": "));
++ if (ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": ") && pEvent->Description() && (strlen(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": ")) > strlen(pEvent->Description()))) {
++ pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": "));
++ pEvent->FixEpgBugs();
++ }
++ }
++ }
++ delete ExtendedEventDescriptors;
++ delete ShortEventDescriptor;
++ continue;
++ }
++#else
+ if (pEvent->Version() == getVersionNumber())
+ continue;
++#endif /* DDEPGENTRY */
+ HasExternalData = ExternalData = true;
+ }
+ // If the new event has a higher table ID, let's skip it.
+@@ -97,7 +198,11 @@ cEIT::cEIT(cSchedules *Schedules, int So
+ if (newEvent)
+ pSchedule->AddEvent(newEvent);
+ if (Tid == 0x4E) { // we trust only the present/following info on the actual TS
++#ifdef USE_DDEPGENTRY
++ if (Setup.DisableVPS == 0 && SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning)
++#else
+ if (SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning)
++#endif /* DDEPGENTRY */
+ pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus(), channel);
+ }
+ if (OnlyRunningStatus)
+@@ -143,8 +248,46 @@ cEIT::cEIT(cSchedules *Schedules, int So
+ }
+ break;
+ case SI::ContentDescriptorTag:
++#ifdef USE_PARENTALRATING
++ {
++ int NumContents = 0;
++ uchar Contents[MAXEVCONTENTS + 1] = { 0 };
++ SI::ContentDescriptor *cd = (SI::ContentDescriptor *)d;
++ SI::ContentDescriptor::Nibble Nibble;
++ for (SI::Loop::Iterator it3; cd->nibbleLoop.getNext(Nibble, it3); ) {
++ if (NumContents < MAXEVCONTENTS) {
++ Contents[NumContents] = ((Nibble.getContentNibbleLevel1() & 0xF) << 4) | (Nibble.getContentNibbleLevel2() & 0xF);
++ NumContents++;
++ }
++ }
++ pEvent->SetContents(Contents);
++ }
++#endif /* PARENTALRATING */
+ break;
+ case SI::ParentalRatingDescriptorTag:
++#ifdef USE_PARENTALRATING
++ {
++ int LanguagePreferenceRating = -1;
++ SI::ParentalRatingDescriptor *prd = (SI::ParentalRatingDescriptor *)d;
++ SI::ParentalRatingDescriptor::Rating Rating;
++ for (SI::Loop::Iterator it3; prd->ratingLoop.getNext(Rating, it3); ) {
++ if (I18nIsPreferredLanguage(Setup.EPGLanguages, Rating.languageCode, LanguagePreferenceRating)) {
++ int rate = (Rating.getRating() & 0xFF);
++ switch (rate) {
++ case 0x01 ... 0x0F: // minimum age = rating + 3 years
++ rate += 3;
++ break;
++ case 0: // undefined
++ case 0x10 ... 0xFF: // defined by the broadcaster
++ default:
++ rate = 0;
++ break;
++ }
++ pEvent->SetParentalRating(rate);
++ }
++ }
++ }
++#endif /* PARENTALRATING */
+ break;
+ case SI::PDCDescriptorTag: {
+ SI::PDCDescriptor *pd = (SI::PDCDescriptor *)d;
+@@ -259,6 +402,62 @@ cEIT::cEIT(cSchedules *Schedules, int So
+ if (LinkChannels)
+ channel->SetLinkChannels(LinkChannels);
+ Modified = true;
++#ifdef USE_DDEPGENTRY
++ //to avoid double epg-entrys from ext and int epg sources :EW
++ if (pEvent && pEvent->TableID() != 0x00) {
++ cEvent *pPreviousEvent = (cEvent *)pSchedule->GetPreviousEvent(pEvent);
++ if (pPreviousEvent) {
++ if (Setup.DoubleEpgAction == 0) {
++ pPreviousEvent->SetStartTime(pEvent->StartTime());
++ pPreviousEvent->SetDuration(pEvent->Duration());
++ if (Setup.DisableVPS == 0) {
++ if (channel)
++ pPreviousEvent->SetRunningStatus(pEvent->RunningStatus(), channel);
++ else
++ pPreviousEvent->SetRunningStatus(pEvent->RunningStatus());
++ }
++ // to use the info of the original epg, update the extern one,
++ // if it has less info
++ char buffer_short_intern[256];
++ char buffer_short_extern[256];
++ int len_short_intern = 0;
++ int len_short_extern = 0;
++ if (pEvent->ShortText())
++ len_short_intern = snprintf (buffer_short_intern, sizeof(buffer_short_intern)-1, "%s", pEvent->ShortText());
++ if (pPreviousEvent->ShortText())
++ len_short_extern = snprintf (buffer_short_extern, sizeof(buffer_short_extern)-1, "%s", pPreviousEvent->ShortText());
++ if (len_short_intern > 0) {
++ if (len_short_extern < 1)
++ pPreviousEvent->SetShortText(buffer_short_intern);
++ else if (len_short_intern > len_short_extern)
++ pPreviousEvent->SetShortText(buffer_short_intern);
++ }
++ if (pEvent->Description()) {
++ char buffer_title_intern[4096];
++ char buffer_title_extern[4096];
++ int len_title_intern = 0;
++ int len_title_extern = 0;
++ if (pEvent->Description())
++ len_title_intern = snprintf (buffer_title_intern, sizeof(buffer_title_intern)-1, "%s", pEvent->Description());
++ if (pPreviousEvent->Description())
++ len_title_extern = snprintf (buffer_title_extern, sizeof(buffer_title_extern)-1, "%s", pPreviousEvent->Description());
++ if (len_title_intern > 0) {
++ if (len_title_extern < 1)
++ pPreviousEvent->SetDescription(buffer_title_intern);
++ else if (len_title_intern > len_title_extern)
++ pPreviousEvent->SetDescription(buffer_title_intern);
++ }
++ }
++ if (pPreviousEvent->Vps() == 0 && pEvent->Vps() != 0)
++ pPreviousEvent->SetVps(pEvent->Vps());
++ pSchedule->DelEvent(pEvent);
++ pPreviousEvent->FixEpgBugs();
++ }
++ else
++ pSchedule->DelEvent(pPreviousEvent);
++ }
++ }
++#endif /* DDEPGENTRY */
+ }
+ if (Empty && Tid == 0x4E && getSectionNumber() == 0)
+ // ETR 211: an empty entry in section 0 of table 0x4E means there is currently no event running
+@@ -296,10 +495,26 @@ cTDT::cTDT(const u_char *Data)
+ time_t sattim = getTime();
+ time_t loctim = time(NULL);
+
++#ifdef USE_SETTIME
++ char timestr[20];
++ struct tm *ptm;
++ struct tm tm_r;
++ ptm = localtime_r(&sattim, &tm_r);
++#endif /* SETTIME */
++
+ int diff = abs(sattim - loctim);
+ if (diff > 2) {
+ mutex.Lock();
+ if (abs(diff - lastDiff) < 3) {
++#ifdef USE_SETTIME
++ if (SetTime) {
++ strftime(timestr, 20, "%m%d%H%M%Y.%S", ptm);
++ cString cmd = cString::sprintf("%s %s %ld", SetTime, timestr, sattim);
++ dsyslog("Executing: %s", *cmd);
++ SystemExec(cmd);
++ }
++ else
++#endif /* SETTIME */
+ if (stime(&sattim) == 0)
+ isyslog("system time changed from %s (%ld) to %s (%ld)", *TimeToString(loctim), loctim, *TimeToString(sattim), sattim);
+ else
+diff -ruNp vdr-1.6.0-2/eitscan.c vdr-1.6.0-2-extensions/eitscan.c
+--- vdr-1.6.0-2/eitscan.c 2006-01-07 15:10:17.000000000 +0100
++++ vdr-1.6.0-2-extensions/eitscan.c 2009-04-09 20:48:26.000000000 +0200
+@@ -151,9 +151,17 @@ void cEITScanner::Process(void)
+ if (Device->ProvidesTransponder(Channel)) {
+ if (!Device->Receiving()) {
+ bool MaySwitchTransponder = Device->MaySwitchTransponder();
++#ifdef USE_LNBSHARE
++ if (MaySwitchTransponder && Device->GetMaxBadPriority(Channel) == -2 || Device->ProvidesTransponderExclusively(Channel) && Device->GetMaxBadPriority(Channel) <= -1 && now - lastActivity > Setup.EPGScanTimeout * 3600) {
++#else
+ if (MaySwitchTransponder || Device->ProvidesTransponderExclusively(Channel) && now - lastActivity > Setup.EPGScanTimeout * 3600) {
++#endif /* LNBSHARE */
+ if (!MaySwitchTransponder) {
++#ifdef USE_LNBSHARE
++ if ((Device == cDevice::ActualDevice() || Device->GetMaxBadPriority(Channel) == -1) && !currentChannel) {
++#else
+ if (Device == cDevice::ActualDevice() && !currentChannel) {
++#endif /* LNBSHARE */
+ cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode
+ currentChannel = Device->CurrentChannel();
+ Skins.Message(mtInfo, tr("Starting EPG scan"));
+diff -ruNp vdr-1.6.0-2/epg.c vdr-1.6.0-2-extensions/epg.c
+--- vdr-1.6.0-2/epg.c 2008-02-16 17:09:12.000000000 +0100
++++ vdr-1.6.0-2-extensions/epg.c 2009-04-09 20:48:26.000000000 +0200
+@@ -112,6 +112,11 @@ cEvent::cEvent(tEventID EventID)
+ components = NULL;
+ startTime = 0;
+ duration = 0;
++#ifdef USE_PARENTALRATING
++ for (int i = 0; i < MAXEVCONTENTS; i++)
++ contents[i] = 0;
++ parentalRating = 0;
++#endif /* PARENTALRATING */
+ vps = 0;
+ SetSeen();
+ }
+@@ -200,6 +205,19 @@ void cEvent::SetDuration(int Duration)
+ duration = Duration;
+ }
+
++#ifdef USE_PARENTALRATING
++void cEvent::SetContents(uchar *Contents)
++{
++ for (int i = 0; i < MAXEVCONTENTS; i++)
++ contents[i] = Contents[i];
++}
++
++void cEvent::SetParentalRating(uchar ParentalRating)
++{
++ parentalRating = ParentalRating;
++}
++#endif /* PARENTALRATING */
++
+ void cEvent::SetVps(time_t Vps)
+ {
+ vps = Vps;
+@@ -255,6 +273,340 @@ cString cEvent::GetVpsString(void) const
+ return buf;
+ }
+
++#ifdef USE_PARENTALRATING
++cString cEvent::GetContentsString(int i) const
++{
++ cString str("");
++ switch (contents[i] & 0xF0) {
++ case EVCONTENTMASK_MOVIEDRAMA:
++ switch (contents[i] & 0x0F) {
++ default:
++ case 0x00:
++ str = cString(tr("Content$Movie/Drama"));
++ break;
++ case 0x01:
++ str = cString(tr("Content$Detective/Thriller"));
++ break;
++ case 0x02:
++ str = cString(tr("Content$Adventure/Western/War"));
++ break;
++ case 0x03:
++ str = cString(tr("Content$Science Fiction/Fantasy/Horror"));
++ break;
++ case 0x04:
++ str = cString(tr("Content$Comedy"));
++ break;
++ case 0x05:
++ str = cString(tr("Content$Soap/Melodrama/Folkloric"));
++ break;
++ case 0x06:
++ str = cString(tr("Content$Romance"));
++ break;
++ case 0x07:
++ str = cString(tr("Content$Serious/Classical/Religious/Historical Movie/Drama"));
++ break;
++ case 0x08:
++ str = cString(tr("Content$Adult Movie/Drama"));
++ break;
++ }
++ break;
++
++ case EVCONTENTMASK_NEWSCURRENTAFFAIRS:
++ switch (contents[i] & 0x0F) {
++ default:
++ case 0x00:
++ str = cString(tr("Content$News/Current Affairs"));
++ break;
++ case 0x01:
++ str = cString(tr("Content$News/Weather Report"));
++ break;
++ case 0x02:
++ str = cString(tr("Content$News Magazine"));
++ break;
++ case 0x03:
++ str = cString(tr("Content$Documentary"));
++ break;
++ case 0x04:
++ str = cString(tr("Content$Discussion/Inverview/Debate"));
++ break;
++ }
++ break;
++
++ case EVCONTENTMASK_SHOW:
++ switch (contents[i] & 0x0F) {
++ default:
++ case 0x00:
++ str = cString(tr("Content$Show/Game Show"));
++ break;
++ case 0x01:
++ str = cString(tr("Content$Game Show/Quiz/Contest"));
++ break;
++ case 0x02:
++ str = cString(tr("Content$Variety Show"));
++ break;
++ case 0x03:
++ str = cString(tr("Content$Talk Show"));
++ break;
++ }
++ break;
++
++ case EVCONTENTMASK_SPORTS:
++ switch (contents[i] & 0x0F) {
++ default:
++ case 0x00:
++ str = cString(tr("Content$Sports"));
++ break;
++ case 0x01:
++ str = cString(tr("Content$Special Event"));
++ break;
++ case 0x02:
++ str = cString(tr("Content$Sport Magazine"));
++ break;
++ case 0x03:
++ str = cString(tr("Content$Football"));
++ break;
++ case 0x04:
++ str = cString(tr("Content$Tennis/Squash"));
++ break;
++ case 0x05:
++ str = cString(tr("Content$Team Sports"));
++ break;
++ case 0x06:
++ str = cString(tr("Content$Athletics"));
++ break;
++ case 0x07:
++ str = cString(tr("Content$Motor Sport"));
++ break;
++ case 0x08:
++ str = cString(tr("Content$Water Sport"));
++ break;
++ case 0x09:
++ str = cString(tr("Content$Winter Sports"));
++ break;
++ case 0x0A:
++ str = cString(tr("Content$Equestrian"));
++ break;
++ case 0x0B:
++ str = cString(tr("Content$Martial Sports"));
++ break;
++ }
++ break;
++
++ case EVCONTENTMASK_CHILDRENYOUTH:
++ switch (contents[i] & 0x0F) {
++ default:
++ case 0x00:
++ str = cString(tr("Content$Children's/Youth Programmes"));
++ break;
++ case 0x01:
++ str = cString(tr("Content$Pre-school Children's Programmes"));
++ break;
++ case 0x02:
++ str = cString(tr("Content$Entertainment Programmes for 6 to 14"));
++ break;
++ case 0x03:
++ str = cString(tr("Content$Entertainment Programmes for 10 to 16"));
++ break;
++ case 0x04:
++ str = cString(tr("Content$Informational/Educational/School Programme"));
++ break;
++ case 0x05:
++ str = cString(tr("Content$Cartoons/Puppets"));
++ break;
++ }
++ break;
++
++ case EVCONTENTMASK_MUSICBALLETDANCE:
++ switch (contents[i] & 0x0F) {
++ default:
++ case 0x00:
++ str = cString(tr("Content$Music/Ballet/Dance"));
++ break;
++ case 0x01:
++ str = cString(tr("Content$Rock/Pop"));
++ break;
++ case 0x02:
++ str = cString(tr("Content$Serious/Classical Music"));
++ break;
++ case 0x03:
++ str = cString(tr("Content$Folk/Tradional Music"));
++ break;
++ case 0x04:
++ str = cString(tr("Content$Jazz"));
++ break;
++ case 0x05:
++ str = cString(tr("Content$Musical/Opera"));
++ break;
++ case 0x06:
++ str = cString(tr("Content$Ballet"));
++ break;
++ }
++ break;
++
++ case EVCONTENTMASK_ARTSCULTURE:
++ switch (contents[i] & 0x0F) {
++ default:
++ case 0x00:
++ str = cString(tr("Content$Arts/Culture"));
++ break;
++ case 0x01:
++ str = cString(tr("Content$Performing Arts"));
++ break;
++ case 0x02:
++ str = cString(tr("Content$Fine Arts"));
++ break;
++ case 0x03:
++ str = cString(tr("Content$Religion"));
++ break;
++ case 0x04:
++ str = cString(tr("Content$Popular Culture/Traditional Arts"));
++ break;
++ case 0x05:
++ str = cString(tr("Content$Literature"));
++ break;
++ case 0x06:
++ str = cString(tr("Content$Film/Cinema"));
++ break;
++ case 0x07:
++ str = cString(tr("Content$Experimental Film/Video"));
++ break;
++ case 0x08:
++ str = cString(tr("Content$Broadcasting/Press"));
++ break;
++ case 0x09:
++ str = cString(tr("Content$New Media"));
++ break;
++ case 0x0A:
++ str = cString(tr("Content$Arts/Culture Magazines"));
++ break;
++ case 0x0B:
++ str = cString(tr("Content$Fashion"));
++ break;
++ }
++ break;
++
++ case EVCONTENTMASK_SOCIALPOLITICALECONOMICS:
++ switch (contents[i] & 0x0F) {
++ default:
++ case 0x00:
++ str = cString(tr("Content$Social/Political/Economics"));
++ break;
++ case 0x01:
++ str = cString(tr("Content$Magazines/Reports/Documentary"));
++ break;
++ case 0x02:
++ str = cString(tr("Content$Economics/Social Advisory"));
++ break;
++ case 0x03:
++ str = cString(tr("Content$Remarkable People"));
++ break;
++ }
++ break;
++
++ case EVCONTENTMASK_EDUCATIONALSCIENCE:
++ switch (contents[i] & 0x0F) {
++ default:
++ case 0x00:
++ str = cString(tr("Content$Education/Science/Factual"));
++ break;
++ case 0x01:
++ str = cString(tr("Content$Nature/Animals/Environment"));
++ break;
++ case 0x02:
++ str = cString(tr("Content$Technology/Natural Sciences"));
++ break;
++ case 0x03:
++ str = cString(tr("Content$Medicine/Physiology/Psychology"));
++ break;
++ case 0x04:
++ str = cString(tr("Content$Foreign Countries/Expeditions"));
++ break;
++ case 0x05:
++ str = cString(tr("Content$Social/Spiritual Sciences"));
++ break;
++ case 0x06:
++ str = cString(tr("Content$Further Education"));
++ break;
++ case 0x07:
++ str = cString(tr("Content$Languages"));
++ break;
++ }
++ break;
++
++ case EVCONTENTMASK_LEISUREHOBBIES:
++ switch (contents[i] & 0x0F) {
++ default:
++ case 0x00:
++ str = cString(tr("Content$Leisure/Hobbies"));
++ break;
++ case 0x01:
++ str = cString(tr("Content$Tourism/Travel"));
++ break;
++ case 0x02:
++ str = cString(tr("Content$Handicraft"));
++ break;
++ case 0x03:
++ str = cString(tr("Content$Motoring"));
++ break;
++ case 0x04:
++ str = cString(tr("Content$Fitness & Health"));
++ break;
++ case 0x05:
++ str = cString(tr("Content$Cooking"));
++ break;
++ case 0x06:
++ str = cString(tr("Content$Advertisement/Shopping"));
++ break;
++ case 0x07:
++ str = cString(tr("Content$Gardening"));
++ break;
++ }
++ break;
++
++ case EVCONTENTMASK_SPECIAL:
++ switch (contents[i] & 0x0F) {
++ case 0x00:
++ str = cString(tr("Content$Original Language"));
++ break;
++ case 0x01:
++ str = cString(tr("Content$Black & White"));
++ break;
++ case 0x02:
++ str = cString(tr("Content$Unpublished"));
++ break;
++ case 0x03:
++ str = cString(tr("Content$Live Broadcast"));
++ break;
++ default:
++ str = cString(tr("Content$Special Characteristics"));
++ break;
++ }
++ break;
++
++ case EVCONTENTMASK_USERDEFINED:
++ switch (contents[i] & 0x0F) {
++ case 0x00:
++ str = cString(tr("Content$Drama")); // UK Freeview
++ break;
++ default:
++ break;
++ }
++ break;
++
++ default:
++ break;
++ }
++ return str;
++}
++
++cString cEvent::GetParentalRatingString(void) const
++{
++ if (parentalRating > 0)
++ return cString::sprintf(tr("Suitable for those aged %d and over"), parentalRating);
++ return NULL;
++}
++#endif /* PARENTALRATING */
++
+ void cEvent::Dump(FILE *f, const char *Prefix, bool InfoOnly) const
+ {
+ if (InfoOnly || startTime + duration + Setup.EPGLinger * 60 >= time(NULL)) {
+@@ -742,6 +1094,28 @@ const cEvent *cSchedule::GetEventAround(
+ return pe;
+ }
+
++#ifdef USE_DDEPGENTRY
++const cEvent *cSchedule::GetPreviousEvent(cEvent *Event) const
++{
++ if (!Event || Event->Duration() == 0 || Event->StartTime() == 0)
++ return NULL;
++ // Returns either the event info to the previous/following event to the given EventID or, if that one can't be found NULL :EW
++ cEvent *pt = NULL;
++ int epgTimeDelta = Setup.DoubleEpgTimeDelta * 60 + 1;
++ for (pt = events.First(); pt; pt = events.Next(pt))
++ if (pt && pt->TableID() == 0x00)
++ if ((Event->StartTime() - pt->StartTime()) > - epgTimeDelta && (Event->StartTime() - pt->StartTime()) < epgTimeDelta) {
++ if ((pt->Duration() + (pt->Duration()/ 5) + 1) > Event->Duration() && (pt->Duration() - (pt->Duration()/ 5) - 1) < Event->Duration())
++ return pt;
++ else if (pt->Title() && Event->Title() && (strcmp(pt->Title(), ".") != 0 && strcmp(Event->Title(), ".") != 0)) {
++ if (strstr(pt->Title(), Event->Title()) != NULL || strstr(Event->Title(), pt->Title()) != NULL)
++ return pt;
++ }
++ }
++ return NULL;
++}
++#endif /* DDEPGENTRY */
++
+ void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Channel)
+ {
+ hasRunning = false;
+diff -ruNp vdr-1.6.0-2/epg.h vdr-1.6.0-2-extensions/epg.h
+--- vdr-1.6.0-2/epg.h 2006-10-07 15:47:19.000000000 +0200
++++ vdr-1.6.0-2-extensions/epg.h 2009-04-09 20:48:26.000000000 +0200
+@@ -50,6 +50,22 @@ class cSchedule;
+
+ typedef u_int32_t tEventID;
+
++#ifdef USE_PARENTALRATING
++#define MAXEVCONTENTS 4
++#define EVCONTENTMASK_MOVIEDRAMA 0x10
++#define EVCONTENTMASK_NEWSCURRENTAFFAIRS 0x20
++#define EVCONTENTMASK_SHOW 0x30
++#define EVCONTENTMASK_SPORTS 0x40
++#define EVCONTENTMASK_CHILDRENYOUTH 0x50
++#define EVCONTENTMASK_MUSICBALLETDANCE 0x60
++#define EVCONTENTMASK_ARTSCULTURE 0x70
++#define EVCONTENTMASK_SOCIALPOLITICALECONOMICS 0x80
++#define EVCONTENTMASK_EDUCATIONALSCIENCE 0x90
++#define EVCONTENTMASK_LEISUREHOBBIES 0xA0
++#define EVCONTENTMASK_SPECIAL 0xB0
++#define EVCONTENTMASK_USERDEFINED 0xF0
++#endif /* PARENTALRATING */
++
+ class cEvent : public cListObject {
+ friend class cSchedule;
+ private:
+@@ -64,6 +80,10 @@ private:
+ cComponents *components; // The stream components of this event
+ time_t startTime; // Start time of this event
+ int duration; // Duration of this event in seconds
++#ifdef USE_PARENTALRATING
++ uchar contents[MAXEVCONTENTS + 1]; // Contents of this event; list is zero-terminated
++ uchar parentalRating; // Parental rating of this event
++#endif /* PARENTALRATING */
+ time_t vps; // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
+ time_t seen; // When this event was last seen in the data stream
+ public:
+@@ -83,6 +103,10 @@ public:
+ time_t StartTime(void) const { return startTime; }
+ time_t EndTime(void) const { return startTime + duration; }
+ int Duration(void) const { return duration; }
++#ifdef USE_PARENTALRATING
++ uchar Contents(int i = 0) const { return (0 <= i && i < MAXEVCONTENTS) ? contents[i] : 0; }
++ uchar ParentalRating(void) const { return parentalRating; }
++#endif /* PARENTALRATING */
+ time_t Vps(void) const { return vps; }
+ time_t Seen(void) const { return seen; }
+ bool SeenWithin(int Seconds) const { return time(NULL) - seen < Seconds; }
+@@ -92,6 +116,10 @@ public:
+ cString GetTimeString(void) const;
+ cString GetEndTimeString(void) const;
+ cString GetVpsString(void) const;
++#ifdef USE_PARENTALRATING
++ cString GetContentsString(int i = 0) const;
++ cString GetParentalRatingString(void) const;
++#endif /* PARENTALRATING */
+ void SetEventID(tEventID EventID);
+ void SetTableID(uchar TableID);
+ void SetVersion(uchar Version);
+@@ -102,6 +130,10 @@ public:
+ void SetComponents(cComponents *Components); // Will take ownership of Components!
+ void SetStartTime(time_t StartTime);
+ void SetDuration(int Duration);
++#ifdef USE_PARENTALRATING
++ void SetContents(uchar *Contents);
++ void SetParentalRating(uchar ParentalRating);
++#endif /* PARENTALRATING */
+ void SetVps(time_t Vps);
+ void SetSeen(void);
+ cString ToDescr(void) const;
+@@ -137,6 +169,9 @@ public:
+ void DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
+ void Cleanup(time_t Time);
+ void Cleanup(void);
++#ifdef USE_DDEPGENTRY
++ const cEvent *GetPreviousEvent(cEvent *Event) const; //:EW
++#endif /* DDEPGENTRY */
+ cEvent *AddEvent(cEvent *Event);
+ void DelEvent(cEvent *Event);
+ void HashEvent(cEvent *Event);
+diff -ruNp vdr-1.6.0-2/HISTORY-liemikuutio vdr-1.6.0-2-extensions/HISTORY-liemikuutio
+--- vdr-1.6.0-2/HISTORY-liemikuutio 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/HISTORY-liemikuutio 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,108 @@
++-----------------------------------
++Liemikuutio for Video Disc Recorder
++
++Maintainer: Rolf Ahrenberg
++-----------------------------------
++
++2006-01-08: Version 1.0
++
++- Based on enAIO with these original patches:
++ Simple recordings sorting by Walter@VDRPortal
++ Alternate rename recordings by Ralf Müller
++ Menu selection by Peter Dittmann
++ Recording length by Tobias Faust
++
++2006-01-15: Version 1.1
++
++- Removed patches already found in vdr-1.3.39.
++
++2006-01-25: Version 1.2
++
++- Added "Main menu command position" feature.
++
++2006-02-05: Version 1.3
++
++- Improved menu selection response.
++
++2006-04-18: Version 1.4
++
++- Added Estonian translation (Thanks to Arthur Konovalov).
++
++2006-04-30: Version 1.5
++
++- Added progress bar view into "What's on now?" menu.
++
++2006-06-06: Version 1.6
++
++- Added French translation (Thanks to ECLiPSE).
++
++2006-06-14: Version 1.7
++
++- Fixed RENR crash.
++
++2006-07-14: Version 1.8
++
++- Fixed RENR/OSD bug.
++
++2006-08-27: Version 1.9
++
++- Some modifications to the recording length and rename recordings
++ patches (Thanks to Firefly).
++- Added k1_k3_jumps_20s patch by Petri Hintukainen.
++
++2006-08-29: Version 1.10
++
++- The cRecording:Title() method now defaults to original formatting.
++
++2006-09-04: Version 1.11
++
++- Removed unused variable from cRecording::Title() method (Thanks to
++ C.Y.M.).
++- Some modifications to the rename recordings patch (Thanks to Firefly).
++
++2006-09-13: Version 1.12
++
++- More modifications to the rename recordings patch (Thanks to Firefly).
++
++2006-10-01: Version 1.13
++
++- Removed unnecessary syslog printing (Thanks to Firefly).
++
++2007-08-14: Version 1.14
++
++- Updated for vdr-1.5.7.
++
++2007-10-16: Version 1.15
++
++- Added recmenu play patch (Thanks to Ville Skyttä).
++- Updated French translation (Thanks to ECLiPSE).
++
++2007-11-04: Version 1.16
++
++- Updated for vdr-1.5.11.
++
++2007-12-08: Version 1.17
++
++- Added binary skip patch.
++- Removed k1_k3_jumps_20s patch.
++
++2008-02-17: Version 1.18
++
++- Updated for vdr-1.5.15.
++
++2008-03-02: Version 1.19
++
++- Modified binary skip to use kPrev and kNext keys and the skip is now
++ always shortened after a direction change (Thanks to Timo Eskola).
++- Readded k1_k3_jumps_20s patch.
++
++2008-04-04: Version 1.20
++
++- Added bitrate information into rename menu.
++- Readded the path editing support of rename recordings patch (Thanks
++ to Firefly).
++
++2008-05-08: Version 1.21
++
++- Fixed rename recordings (Thanks to Firefly).
++- Added a DVB subtitles hack for old recordings (Thanks to Anssi Hannula).
+diff -ruNp vdr-1.6.0-2/iconpatch.c vdr-1.6.0-2-extensions/iconpatch.c
+--- vdr-1.6.0-2/iconpatch.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/iconpatch.c 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,31 @@
++#ifdef USE_WAREAGLEICON
++
++#include "iconpatch.h"
++
++#include <langinfo.h>
++#include <locale.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++bool IsLangUtf8(void)
++{
++ char *CodeSet = NULL;
++ if (setlocale(LC_CTYPE, ""))
++ CodeSet = nl_langinfo(CODESET);
++ else {
++ char *LangEnv = getenv("LANG"); // last resort in case locale stuff isn't installed
++ if (LangEnv) {
++ CodeSet = strchr(LangEnv, '.');
++ if (CodeSet)
++ CodeSet++; // skip the dot
++ }
++ }
++
++ if (CodeSet && strcasestr(CodeSet, "UTF-8") != 0)
++ return true;
++
++ return false;
++}
++
++#endif /* WAREAGLEICON */
+diff -ruNp vdr-1.6.0-2/iconpatch.h vdr-1.6.0-2-extensions/iconpatch.h
+--- vdr-1.6.0-2/iconpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/iconpatch.h 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,73 @@
++#ifdef USE_WAREAGLEICON
++/*
++ * iconpatch.h: Information of iconpatch
++ *
++ * Diese Datei ist die Übersichtsdatei für den Iconpatch.
++ * Hier werden kleine Infos abgelegt.
++ * Der Iconpatch ändert die Dateien:
++ * iconpatch.h
++ * menu.c
++ * recording.c
++ * fontosd.c
++ *
++ */
++
++// Iconpatch-Variablen - Anfang
++#define ICON_NUMBERSIGN "\x23"
++#define ICON_ASTERISK "\x2A"
++#define ICON_GREATER "\x3E"
++#define ICON_EXCLAM "\x21"
++#define ICON_PLUSMINUS "\xB1"
++
++#define ICON_RESUME "\x80"
++#define ICON_DVD "\x81"
++#define ICON_FOLDER "\x82"
++#define ICON_BLANK "\x83"
++#define ICON_CUTTING "\x84"
++#define ICON_MOVE_FILE "\x85"
++#define ICON_MOVE_FOLDER "\x86"
++#define ICON_BAR_START "\x87"
++#define ICON_BAR_FILLED "\x88"
++#define ICON_BAR_CLEAR "\x89"
++#define ICON_BAR_END "\x8A"
++#define ICON_REC "\x8B"
++#define ICON_CLOCK "\x8C"
++#define ICON_TV_CRYPTED "\x8D"
++#define ICON_RADIO "\x8E"
++#define ICON_TV "\x8F"
++#define ICON_NEW "\x90"
++#define ICON_ARROW "\x91"
++#define ICON_RUNNING "\x92"
++#define ICON_VPS "\x93"
++#define ICON_CLOCK_UH "\x94"
++#define ICON_CLOCK_LH "\x95"
++
++// UTF-8 Icons
++#define ICON_RESUME_UTF8 "\uE000"
++#define ICON_DVD_UTF8 "\uE001"
++#define ICON_FOLDER_UTF8 "\uE002"
++#define ICON_BLANK_UTF8 "\uE003"
++#define ICON_CUTTING_UTF8 "\uE004"
++#define ICON_MOVE_FILE_UTF8 "\uE005"
++#define ICON_MOVE_FOLDER_UTF8 "\uE006"
++#define ICON_BAR_START_UTF8 "\uE007"
++#define ICON_BAR_FILLED_UTF8 "\uE008"
++#define ICON_BAR_EMPTY_UTF8 "\uE009"
++#define ICON_BAR_CLOSE_UTF8 "\uE00A"
++#define ICON_REC_UTF8 "\uE00B"
++#define ICON_CLOCK_UTF8 "\uE00C"
++#define ICON_TV_CRYPTED_UTF8 "\uE00D"
++#define ICON_RADIO_UTF8 "\uE00E"
++#define ICON_TV_UTF8 "\uE00F"
++#define ICON_NEW_UTF8 "\uE010"
++#define ICON_ARROW_UTF8 "\uE011"
++#define ICON_RUNNING_UTF8 "\uE012"
++#define ICON_VPS_UTF8 "\uE013"
++#define ICON_CLOCK_UH_UTF8 "\uE014"
++#define ICON_CLOCK_LH_UTF8 "\uE015"
++
++// Iconpatch-Variablen - Ende
++
++bool IsLangUtf8(void);
++
++#endif /* WAREAGLEICON */
+diff -ruNp vdr-1.6.0-2/keys.h vdr-1.6.0-2-extensions/keys.h
+--- vdr-1.6.0-2/keys.h 2007-08-26 14:34:50.000000000 +0200
++++ vdr-1.6.0-2-extensions/keys.h 2009-04-09 20:48:26.000000000 +0200
+@@ -71,6 +71,10 @@ enum eKeys { // "Up" and "Down" must be
+ #define kEditCut k2
+ #define kEditTest k8
+
++#ifdef USE_DVDARCHIVE
++#define kDvdChapterJumpForward k6
++#define kDvdChapterJumpBack k4
++#endif /* DVDARCHIVE */
+ #define RAWKEY(k) (eKeys((k) & ~k_Flags))
+ #define ISRAWKEY(k) ((k) != kNone && ((k) & k_Flags) == 0)
+ #define NORMALKEY(k) (eKeys((k) & ~k_Repeat))
+diff -ruNp vdr-1.6.0-2/lirc.c vdr-1.6.0-2-extensions/lirc.c
+--- vdr-1.6.0-2/lirc.c 2006-05-28 10:48:13.000000000 +0200
++++ vdr-1.6.0-2-extensions/lirc.c 2009-04-09 20:48:26.000000000 +0200
+@@ -12,6 +12,9 @@
+ #include "lirc.h"
+ #include <netinet/in.h>
+ #include <sys/socket.h>
++#ifdef USE_LIRCSETTINGS
++#include "config.h"
++#endif /* LIRCSETTINGS */
+
+ #define REPEATDELAY 350 // ms
+ #define REPEATFREQ 100 // ms
+@@ -94,7 +97,11 @@ void cLircRemote::Action(void)
+ continue;
+ }
+ if (count == 0) {
++#ifdef USE_LIRCSETTINGS
++ if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < (unsigned int)Setup.LircRepeatDelay)
++#else
+ if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < REPEATDELAY)
++#endif /* LIRCSETTINGS */
+ continue; // skip keys coming in too fast
+ if (repeat)
+ Put(LastKeyName, false, true);
+@@ -104,18 +111,34 @@ void cLircRemote::Action(void)
+ timeout = -1;
+ }
+ else {
++#ifdef USE_LIRCSETTINGS
++ if (LastTime.Elapsed() < (unsigned int)Setup.LircRepeatFreq)
++#else
+ if (LastTime.Elapsed() < REPEATFREQ)
++#endif /* LIRCSETTINGS */
+ continue; // repeat function kicks in after a short delay (after last key instead of first key)
++#ifdef USE_LIRCSETTINGS
++ if (FirstTime.Elapsed() < (unsigned int)Setup.LircRepeatDelay)
++#else
+ if (FirstTime.Elapsed() < REPEATDELAY)
++#endif /* LIRCSETTINGS */
+ continue; // skip keys coming in too fast (for count != 0 as well)
+ repeat = true;
++#ifdef USE_LIRCSETTINGS
++ timeout = Setup.LircRepeatDelay;
++#else
+ timeout = REPEATDELAY;
++#endif /* LIRCSETTINGS */
+ }
+ LastTime.Set();
+ Put(KeyName, repeat);
+ }
+ else if (repeat) { // the last one was a repeat, so let's generate a release
++#ifdef USE_LIRCSETTINGS
++ if (LastTime.Elapsed() >= (unsigned int)Setup.LircRepeatTimeout) {
++#else
+ if (LastTime.Elapsed() >= REPEATTIMEOUT) {
++#endif /* LIRCSETTINGS */
+ Put(LastKeyName, false, true);
+ repeat = false;
+ *LastKeyName = 0;
+diff -ruNp vdr-1.6.0-2/livebuffer.c vdr-1.6.0-2-extensions/livebuffer.c
+--- vdr-1.6.0-2/livebuffer.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/livebuffer.c 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,1912 @@
++#ifdef USE_LIVEBUFFER
++#include "livebuffer.h"
++#include "menu.h"
++#include "recording.h"
++#include "transfer.h"
++
++#define RECORDERBUFSIZE MEGABYTE(2)
++
++// --- cFileName -------------------------------------------------------------
++
++#include <errno.h>
++#include <unistd.h>
++#include "videodir.h"
++
++#define MAXFILESPERRECORDING 255
++#define RECORDFILESUFFIX "/%03d.vdr"
++#define RECORDFILESUFFIXLEN 20
++
++cFileName64::cFileName64(const char *FileName, bool Record, bool Blocking)
++{
++ file = NULL;
++ record = Record;
++ blocking = Blocking;
++ // Prepare the file name:
++ fileName = MALLOC(char, strlen(FileName) + RECORDFILESUFFIXLEN);
++ if (!fileName) {
++ esyslog("ERROR: can't copy file name '%s'", fileName);
++ return;
++ }
++ strcpy(fileName, FileName);
++ pFileNumber = fileName + strlen(fileName);
++
++ fileNumber = 1;
++ while (true) {
++ sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
++ if (record) {
++ if (access(fileName, F_OK) == 0) {
++ struct stat64 buf;
++ if (stat64(fileName, &buf) == 0) {
++ fileNumber++;
++ continue;
++ }
++ }
++ else if (errno != ENOENT)
++ LOG_ERROR_STR(fileName);
++ }
++ break;
++ }
++}
++
++cFileName64::~cFileName64()
++{
++ Close();
++ free(fileName);
++}
++
++void cFileName64::SetNumber(int Number)
++{
++ fileNumber = Number;
++ sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
++}
++
++cUnbufferedFile64 *cFileName64::Open(void)
++{
++ if (!file) {
++ int BlockingFlag = blocking ? 0 : O_NONBLOCK;
++ if (record) {
++ file = cUnbufferedFile64::Create(fileName, O_RDWR | O_CREAT | BlockingFlag);
++ if (!file)
++ LOG_ERROR_STR(fileName);
++ }
++ else {
++ if (access(fileName, R_OK) == 0) {
++ file = cUnbufferedFile64::Create(fileName, O_RDONLY | BlockingFlag);
++ if (!file)
++ LOG_ERROR_STR(fileName);
++ }
++ else if (errno != ENOENT)
++ LOG_ERROR_STR(fileName);
++ }
++ }
++ return file;
++}
++
++void cFileName64::Close(void)
++{
++ if (file) {
++ if (file->Close() < 0)
++ LOG_ERROR_STR(fileName);
++ delete file;
++ file = NULL;
++ }
++}
++
++void cFileName64::CloseAndRemove(void)
++{
++ Close();
++ remove(fileName);
++}
++
++// --- cUnbufferedFile64 -----------------------------------------------------
++
++#define USE_FADVISE
++
++#define WRITE_BUFFER KILOBYTE(800)
++
++cUnbufferedFile64::cUnbufferedFile64(void)
++{
++ fd = -1;
++}
++
++cUnbufferedFile64::~cUnbufferedFile64()
++{
++ Close();
++}
++
++int cUnbufferedFile64::Open(const char *FileName, int Flags, mode_t Mode)
++{
++ Close();
++ fd = open64(FileName, Flags, Mode);
++ curpos = 0;
++#ifdef USE_FADVISE
++ begin = lastpos = ahead = 0;
++ cachedstart = 0;
++ cachedend = 0;
++ readahead = KILOBYTE(128);
++ written = 0;
++ totwritten = 0;
++ if (fd >= 0)
++ posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
++#endif
++ return fd;
++}
++
++int cUnbufferedFile64::Close(void)
++{
++#ifdef USE_FADVISE
++ if (fd >= 0) {
++ if (totwritten)
++ fdatasync(fd);
++ posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
++ }
++#endif
++ int OldFd = fd;
++ fd = -1;
++ return close(OldFd);
++}
++
++
++#define FADVGRAN KILOBYTE(4)
++#define READCHUNK MEGABYTE(8)
++
++int cUnbufferedFile64::FadviseDrop(off64_t Offset, off64_t Len)
++{
++ return posix_fadvise64(fd, Offset - (FADVGRAN - 1), Len + (FADVGRAN - 1) * 2, POSIX_FADV_DONTNEED);
++}
++
++
++off64_t cUnbufferedFile64::Seek(off64_t Offset, int Whence)
++{
++ if (Whence == SEEK_SET && Offset == curpos)
++ return curpos;
++ curpos = lseek64(fd, Offset, Whence);
++ return curpos;
++}
++
++ssize_t cUnbufferedFile64::Read(void *Data, size_t Size)
++{
++ if (fd >= 0) {
++#ifdef USE_FADVISE
++ off64_t jumped = curpos-lastpos;
++ if ((cachedstart < cachedend) && (curpos < cachedstart || curpos > cachedend)) {
++ FadviseDrop(cachedstart, cachedend-cachedstart);
++ cachedstart = curpos;
++ cachedend = curpos;
++ }
++ cachedstart = min(cachedstart, curpos);
++#endif
++ ssize_t bytesRead = safe_read(fd, Data, Size);
++#ifdef USE_FADVISE
++ if (bytesRead > 0) {
++ curpos += bytesRead;
++ cachedend = max(cachedend, curpos);
++
++ if (jumped >= 0 && jumped <= (off64_t)readahead) {
++ if (ahead - curpos < (off64_t)(readahead / 2)) {
++ posix_fadvise64(fd, curpos, readahead, POSIX_FADV_WILLNEED);
++ ahead = curpos + readahead;
++ cachedend = max(cachedend, ahead);
++ }
++ if (readahead < Size * 32) {
++ readahead = Size * 32;
++ }
++ }
++ else
++ ahead = curpos;
++ }
++
++ if (cachedstart < cachedend) {
++ if (curpos - cachedstart > READCHUNK * 2) {
++ FadviseDrop(cachedstart, curpos - READCHUNK - cachedstart);
++ cachedstart = curpos - READCHUNK;
++ }
++ else if (cachedend > ahead && cachedend - curpos > READCHUNK * 2) {
++ FadviseDrop(curpos + READCHUNK, cachedend - (curpos + READCHUNK));
++ cachedend = curpos + READCHUNK;
++ }
++ }
++ lastpos = curpos;
++#endif
++ return bytesRead;
++ }
++ return -1;
++}
++
++ssize_t cUnbufferedFile64::Write(const void *Data, size_t Size)
++{
++ if (fd >=0) {
++ ssize_t bytesWritten = safe_write(fd, Data, Size);
++#ifdef USE_FADVISE
++ if (bytesWritten > 0) {
++ begin = min(begin, curpos);
++ curpos += bytesWritten;
++ written += bytesWritten;
++ lastpos = max(lastpos, curpos);
++ if (written > WRITE_BUFFER) {
++ if (lastpos > begin) {
++ off64_t headdrop = min(begin, (off64_t) WRITE_BUFFER * 2L);
++ posix_fadvise64(fd, begin - headdrop, lastpos - begin + headdrop, POSIX_FADV_DONTNEED);
++ }
++ begin = lastpos = curpos;
++ totwritten += written;
++ written = 0;
++ if (totwritten > MEGABYTE(32)) {
++ off64_t headdrop = min((off64_t) (curpos - totwritten), (off64_t) totwritten * 2L);
++ posix_fadvise64(fd, curpos - totwritten - headdrop, totwritten + headdrop, POSIX_FADV_DONTNEED);
++ totwritten = 0;
++ }
++ }
++ }
++#endif
++ return bytesWritten;
++ }
++ return -1;
++}
++
++cUnbufferedFile64 *cUnbufferedFile64::Create(const char *FileName, int Flags, mode_t Mode)
++{
++ cUnbufferedFile64 *File = new cUnbufferedFile64;
++ if (File->Open(FileName, Flags, Mode) < 0) {
++ delete File;
++ File = NULL;
++ }
++ return File;
++}
++
++// --- cLiveIndex ------------------------------------------------------------
++
++cLiveIndex::cLiveIndex(int BufSize)
++{
++ bufSize = BufSize;
++ headblock = tailblock = new block;
++ headblock->previous = headblock->next = headblock;
++ head = tail = 0;
++ blank = 0;
++ blockCount = 1;
++ writtenCount = delCount = 0;
++ lastWrittenSize = lastCount = 0;
++ fileSize = 0;
++ lastFrame = -1;
++ DPos = -1;
++ DCount = 0;
++ start = 0;
++ RwLock = new cRwLock(true);
++}
++
++cLiveIndex::~cLiveIndex()
++{
++ while (tailblock != headblock) {
++ block *n = tailblock->next;
++ delete tailblock;
++ tailblock = n;
++ }
++ delete headblock;
++ delete RwLock;
++}
++
++void cLiveIndex::Add(uchar pt, int pos)
++{
++ RwLock->Lock(true);
++ DCount=0;
++ DPos = pos;
++ headblock->Frame[head].offset = pos;
++ headblock->Frame[head].written = false;
++ headblock->Frame[head].pt = pt;
++ head++;
++ lastFrame++;
++ if (head == INDEXBLOCKSIZE) {
++ block* b = new block;
++ b->previous = headblock;
++ b->next = b;
++ headblock->next = b;
++ headblock = b;
++ head = 0;
++ blockCount++;
++ }
++ RwLock->Unlock();
++}
++
++void cLiveIndex::AddData(int pos)
++{
++ RwLock->Lock(true);
++ DCount=pos-DPos;
++ RwLock->Unlock();
++}
++
++void cLiveIndex::Delete(off64_t FilePos)
++{
++ RwLock->Lock(true);
++ if (tailblock->Frame[tail].offset <= FilePos) {
++ if (tailblock->Frame[tail].offset + MAXFRAMESIZE >= FilePos) {
++ int old_offset = tailblock->Frame[tail].offset;
++ while (tailblock->Frame[tail].written && tailblock->Frame[tail].offset < FilePos && tailblock->Frame[tail].offset >= old_offset) {
++ tail++;
++ delCount++;
++ if (tail == INDEXBLOCKSIZE) {
++ block *b = tailblock->next;
++ b->previous = b;
++ if (tailblock != headblock)
++ delete tailblock;
++ tailblock = b;
++ tail = 0;
++ blockCount--;
++ }
++ }
++ }
++ }
++ else {
++ if (tailblock->Frame[tail].offset - MAXFRAMESIZE > FilePos) {
++ int old_offset = tailblock->Frame[tail].offset;
++ while (tailblock->Frame[tail].written && (tailblock->Frame[tail].offset < FilePos || tailblock->Frame[tail].offset >= old_offset)) {
++ tail++;
++ delCount++;
++ if (tail == INDEXBLOCKSIZE) {
++ block *b = tailblock->next;
++ b->previous = b;
++ if (tailblock != headblock)
++ delete tailblock;
++ tailblock = b;
++ tail = 0;
++ blockCount--;
++ }
++ }
++ }
++ }
++ RwLock->Unlock();
++}
++
++int cLiveIndex::DelFirst(void)
++{
++ RwLock->Lock(true);
++ tail++;
++ delCount++;
++ writtenCount++;
++ if (tail == INDEXBLOCKSIZE) {
++ block *b = tailblock->next;
++ b->previous = b;
++ if (tailblock != headblock)
++ delete tailblock;
++ tailblock = b;
++ tail = 0;
++ blockCount--;
++ }
++ int r = tailblock->Frame[tail].offset;
++ RwLock->Unlock();
++ return r;
++}
++
++void cLiveIndex::Clear(void)
++{
++ RwLock->Lock(true);
++ while (tailblock != headblock) {
++ block *n = tailblock->next;
++ delete tailblock;
++ tailblock = n;
++ }
++ headblock->previous = headblock;
++ head = tail = blank = 0;
++ blockCount = 1;
++ writtenCount = delCount = 0;
++ DPos = -1;
++ DCount = 0;
++ start = 0;
++ lastFrame = -1;
++ lastWrittenSize = lastCount = 0;
++ fileSize = 0;
++ RwLock->Unlock();
++}
++
++int cLiveIndex::NextWrite(void)
++{
++ RwLock->Lock(false);
++ block *b;
++ int i = GetFrame(&b,writtenCount);
++ int off = 0;
++ if (i+1 == INDEXBLOCKSIZE)
++ off = b->next->Frame[0].offset;
++ else
++ off = b->Frame[i+1].offset;
++ int r = 0;
++ if (off < b->Frame[i].offset)
++ r = bufSize - b->Frame[i].offset - blank;
++ else
++ r = off - b->Frame[i].offset;
++ RwLock->Unlock();
++ return r;
++}
++
++void cLiveIndex::WrittenTo(off64_t FilePos, int Count)
++{
++ RwLock->Lock(true);
++ lastWrittenSize=Count;
++ block *b;
++ int i = GetFrame(&b, writtenCount);
++ writtenCount++;
++ if (FilePos == 0) {
++ if (i)
++ fileSize = b->Frame[i-1].offset + lastCount;
++ else if (b->previous != b)
++ fileSize = b->previous->Frame[INDEXBLOCKSIZE-1].offset + lastCount;
++ }
++ b->Frame[i].offset = FilePos;
++ b->Frame[i].written = true;
++ RwLock->Unlock();
++ lastCount = Count;
++}
++
++int cLiveIndex::GetFrame(block** Block, int Number)
++{
++ int bc = 0;
++ int i = Number-delCount;
++ if (INDEXBLOCKSIZE-tail <= i)
++ bc = 1 + (i-INDEXBLOCKSIZE+tail)/INDEXBLOCKSIZE;
++ i-= bc*INDEXBLOCKSIZE-tail;
++ block *b = tailblock;
++ for (int j=0; j<bc; j++)
++ b = b->next;
++ *Block = b;
++ return i;
++}
++
++off64_t cLiveIndex::GetOffset(int Number)
++{
++ RwLock->Lock(false);
++ off64_t r = -1;
++ if (HasFrame(Number)) {
++ block *b;
++ int i = GetFrame(&b, Number);
++ r = b->Frame[i].offset;
++ }
++ else if (HasFrame(Number-1))
++ r = DPos;
++ RwLock->Unlock();
++ return r;
++}
++
++int cLiveIndex::Size(int Number, uchar *PictureType)
++{
++ int r = -1;
++ RwLock->Lock(false);
++ if (HasFrame(Number)) {
++ if (Number+1 == writtenCount)
++ r = lastWrittenSize;
++ else {
++ block *b;
++ int i = GetFrame(&b, Number);
++ if (PictureType)
++ *PictureType = b->Frame[i].pt;
++ off64_t off = 0;
++ if (i+1 == INDEXBLOCKSIZE)
++ off = b->next->Frame[0].offset;
++ else
++ off = b->Frame[i+1].offset;
++ if (off < b->Frame[i].offset) {
++ if (b->Frame[i].written)
++ r = fileSize - b->Frame[i].offset;
++ else
++ r = bufSize - b->Frame[i].offset - blank;
++ }
++ else
++ r = off - b->Frame[i].offset;
++ }
++ }
++ else if (HasFrame(Number-1) && DPos >= 0)
++ r = DCount;
++ RwLock->Unlock();
++ return r;
++}
++
++int cLiveIndex::GetNextIFrame(int Index, bool Forward)
++{
++ int d = Forward ? 1 : -1;
++ Index += d;
++ while (Index >= delCount && Index < lastFrame) {
++ block *b;
++ RwLock->Lock(false);
++ int i = GetFrame(&b, Index);
++ uchar type = b->Frame[i].pt;
++ RwLock->Unlock();
++ if (type == I_FRAME)
++ return Index;
++ Index +=d;
++ }
++ return -1;
++}
++
++int cLiveIndex::FindIFrame(int64_t PTS, uchar *Buffer)
++{
++ int r = -1;
++ RwLock->Lock(false);
++ int Index=writtenCount;
++ int64_t pts=0;
++ do {
++ Index = GetNextIFrame(Index, true);
++ if (Index < 0)
++ break;
++ block *b;
++ int i = GetFrame(&b, Index);
++ uchar *p = &Buffer[b->Frame[i].offset];
++ if (p[0]==0x00 && p[1]==0x00 && p[2]==0x01 && (p[7] & 0x80) && p[3]>=0xC0 && p[3]<=0xEF) {
++ pts = (int64_t) (p[ 9] & 0x0E) << 29 ;
++ pts |= (int64_t) p[ 10] << 22 ;
++ pts |= (int64_t) (p[ 11] & 0xFE) << 14 ;
++ pts |= (int64_t) p[ 12] << 7 ;
++ pts |= (int64_t) (p[ 13] & 0xFE) >> 1 ;
++ }
++ } while (pts!=PTS);
++ if (pts==PTS)
++ r = Index;
++ RwLock->Unlock();
++ return r;
++}
++
++void cLiveIndex::Switched(void)
++{
++ start = Last();
++}
++
++// --- cLiveFileReader -------------------------------------------------------
++
++cLiveFileReader::cLiveFileReader(const char *FileName, int Number)
++{
++ fileName = new cFileName64(FileName, false);
++ fileName->SetNumber(Number);
++ readFile = fileName->Open();
++ buffer = NULL;
++ filePos = 0;
++ Start();
++}
++
++cLiveFileReader::~cLiveFileReader()
++{
++ Cancel(3);
++ delete fileName;
++}
++
++void cLiveFileReader::Action(void)
++{
++ while (Running()) {
++ Lock();
++ if (buffer && !hasData) {
++ int r = readFile->Read(buffer + length, wanted - length);
++ if (r >= 0) {
++ length += r;
++ filePos += r;
++ if (length == wanted)
++ hasData = true;
++ }
++ else if (r < 0 && FATALERRNO) {
++ LOG_ERROR;
++ length = r;
++ hasData = true;
++ }
++ }
++ Unlock();
++ newSet.Wait(1000);
++ }
++}
++
++int cLiveFileReader::Read(uchar **Buffer, off64_t FilePos, int Size)
++{
++ if (buffer && hasData) {
++ *Buffer = buffer;
++ buffer = NULL;
++ return length;
++ }
++ if (!buffer) {
++ uchar *b = MALLOC(uchar, Size);
++ if (filePos != FilePos) {
++ filePos = FilePos;
++ readFile->Seek(FilePos,0);
++ }
++ wanted = Size;
++ length = 0;
++ hasData = false;
++ buffer = b;
++ newSet.Signal();
++ }
++ return -1;
++}
++
++void cLiveFileReader::Clear(void)
++{
++ Lock();
++ delete buffer;
++ buffer = NULL;
++ Unlock();
++}
++
++// --- cLiveFileWriter -------------------------------------------------------
++
++cLiveFileWriter::cLiveFileWriter(const char *FileName)
++{
++ MakeDirs(FileName, true);
++ SpinUpDisk(FileName);
++ fileName = new cFileName64(FileName, true);
++ writeFile = fileName->Open();
++ buffer = NULL;
++ filePos = fileSize = 0;
++ lastCheck = time(NULL);
++ full = false;
++ Start();
++}
++
++cLiveFileWriter::~cLiveFileWriter()
++{
++ Cancel(3);
++ fileName->CloseAndRemove();
++ delete fileName;
++}
++
++void cLiveFileWriter::Action(void)
++{
++ while (Running()) {
++ Lock();
++ if (buffer && !hasWritten) {
++ int r = writeFile->Write(buffer, wantWrite);
++ if (r >= 0) {
++ filePos += r;
++ hasWritten = true;
++ }
++ else if (r < 0 && FATALERRNO) {
++ LOG_ERROR;
++ hasWritten = true;
++ }
++ }
++ Unlock();
++ newSet.Wait(1000);
++ }
++}
++
++#define MINFREE 600
++
++bool cLiveFileWriter::LowDiskSpace()
++{
++ if (!full && time (NULL) > lastCheck + 60 || full && filePos >= fileSize) {
++ int Free = FreeDiskSpaceMB(fileName->Name());
++ lastCheck = time(NULL);
++ if (Free < MINFREE)
++ return true;
++ }
++ return false;
++}
++
++off64_t cLiveFileWriter::Write(uchar *Buffer, int Size, bool Wait)
++{
++ if (buffer && hasWritten) {
++ buffer = NULL;
++ return filePos;
++ }
++ if (!buffer) {
++ if (filePos + Size > (off64_t)Setup.LiveBufferSize*1024*1024 || LowDiskSpace()) {
++ fileSize = filePos > fileSize ? filePos : fileSize;
++ filePos = 0;
++ writeFile->Seek(0,0);
++ full = true;
++ }
++ if (Wait) {
++ writeFile->Write(Buffer, Size);
++ filePos += Size;
++ return filePos;
++ }
++ else {
++ wantWrite = Size;
++ hasWritten = false;
++ buffer = Buffer;
++ newSet.Signal();
++ }
++ }
++ if (Wait) {
++ while (!hasWritten)
++ usleep(5);
++ buffer = NULL;
++ return filePos;
++ }
++ return -1;
++}
++
++void cLiveFileWriter::Clear(void)
++{
++ Lock();
++ buffer = NULL;
++ filePos = 0;
++ full = false;
++ writeFile->Seek(0,0);
++ Unlock();
++}
++
++// --- cLiveCutterThread -----------------------------------------------------
++
++cLiveCutterThread::cLiveCutterThread(const char *FileName, int FileNumber, cLiveBuffer *LiveBuffer, int StartFrame, int EndFrame)
++{
++ readFile = NULL;
++
++ writeFileName = new cFileName64(FileName, true);
++ writeFileName->SetNumber(FileNumber);
++ writeFile = writeFileName->Open();
++
++ liveBuffer = LiveBuffer;
++ startFrame = StartFrame;
++ endFrame = EndFrame;
++ Start();
++}
++
++cLiveCutterThread::~cLiveCutterThread()
++{
++ Cancel(3);
++ if (readFile)
++ readFileName->Close();
++ writeFileName->Close();
++}
++
++void cLiveCutterThread::Action(void)
++{
++ int Frame = startFrame;
++ int readPos = 0;
++ int writePos = 0;
++ while (Running() && Frame < endFrame) {
++ uchar pt;
++ liveBuffer->index->RwLock->Lock(false);
++ int size = liveBuffer->index->Size(Frame, &pt);
++ if (pt == I_FRAME && writePos > MEGABYTE(Setup.MaxVideoFileSize)) {
++ int Number = writeFileName->Number();
++ writeFileName->Close();
++ writeFileName->SetNumber(Number+1);
++ writeFile = writeFileName->Open();
++ writePos = 0;
++ }
++ if (liveBuffer->index->IsWritten(Frame)) {
++ if (!readFile) {
++ readFileName = new cFileName64(liveBuffer->filestring, false);
++ readFileName->SetNumber(liveBuffer->fileWriter->FileNumber());
++ readFile = readFileName->Open();
++ }
++ uchar b[MAXFRAMESIZE];
++ if (liveBuffer->index->GetOffset(Frame) != readPos) {
++ readPos = liveBuffer->index->GetOffset(Frame);
++ readFile->Seek(readPos,0);
++ }
++ int c = readFile->Read(b, size);
++ readPos += c;
++ writeFile->Write(b,c);
++ writePos+=size;
++ if (c != size)
++ writeFile->Seek(writePos,0);
++ }
++ else {
++ writeFile->Write(&liveBuffer->buffer[liveBuffer->index->GetOffset(Frame)], size);
++ writePos+=size;
++ }
++ liveBuffer->index->RwLock->Unlock();
++ Frame++;
++ cCondWait::SleepMs(5);
++ }
++ if (readFile)
++ readFileName->Close();
++ writeFileName->Close();
++}
++
++// --- cLiveBuffer -----------------------------------------------------------
++
++cLiveBuffer::cLiveBuffer(const char *FileName, cRemux *Remux, const cChannel *Channel, int Number)
++:cThread("livebuffer")
++{
++ number = Number;
++ bufSize = (Setup.InRAM && Number == 0) ? MEGABYTE(Setup.MemBufSize) : LIVEBUFSIZE;
++ buffer = MALLOC(uchar, bufSize);
++ writeHD = !(Setup.InRAM && Number == 0);
++ lastRead = 0;
++ if (!buffer) {
++ dsyslog("Couldn't reserve memory for livebuffer\n");
++ bufSize = LIVEBUFSIZE;
++ buffer = MALLOC(uchar, bufSize);
++ }
++ channel = Channel;
++ index = new cLiveIndex(bufSize);
++ remux = Remux;
++ filestring = strdup(FileName);
++ head = tail = blank = 0;
++ startFrame = -1;
++ liveCutter = NULL;
++ fileWriter = NULL;
++ fileReader = NULL;
++ if (writeHD) {
++ fileWriter = new cLiveFileWriter(FileName);
++ fileReader = new cLiveFileReader(FileName, fileWriter->FileNumber());
++ }
++ status = 0;
++ Start();
++}
++
++cLiveBuffer::~cLiveBuffer()
++{
++ Cancel(3);
++ delete liveCutter;
++ delete fileReader;
++ delete index;
++ delete fileWriter;
++ free(filestring);
++ free(buffer);
++}
++
++void cLiveBuffer::SetStatus(int s)
++{
++ if (s == -1 || status != 2 && s == 0)
++ status = 0;
++ if (status == 0 && s == 1)
++ status = 1;
++ if (s == 2)
++ status = 2;
++ if (s == -2 && status == 0)
++ status = -1;
++}
++
++void cLiveBuffer::Write(bool Wait)
++{
++ int wcount = index->NextWrite();
++ off64_t pos=fileWriter->Write(&buffer[tail], wcount, Wait);
++ if (pos>=0) {
++ index->WrittenTo(pos-wcount, wcount);
++ tail += wcount;
++ if (tail + MAXFRAMESIZE > bufSize) {
++ tail = 0;
++ blank = 0;
++ index->SetBlank(blank);
++ }
++ if (fileWriter->Full())
++ index->Delete(pos);
++ }
++}
++
++void cLiveBuffer::Store(uchar *Data, int Count)
++{
++
++ if (pictureType != NO_PICTURE) {
++ if (head + MAXFRAMESIZE > bufSize) {
++ blank = bufSize - head;
++ head = 0;
++ index->SetBlank(blank);
++ }
++ index->Add(pictureType,head);
++ }
++ else
++ index->AddData(head);
++ memcpy(&buffer[head], Data, Count);
++ head += Count;
++
++ int free = head >= tail ? max(tail,bufSize-head) : tail - head;
++
++ while (free < MAXFRAMESIZE) {
++ if (writeHD)
++ Write(true);
++ else {
++ tail = index->DelFirst();
++ if (tail == 0) {
++ blank = 0;
++ index->SetBlank(blank);
++ }
++ if (Setup.ExtendBuffer && lastRead < index->delCount+25 && status >= 0) {
++ writeHD = true;
++ fileWriter = new cLiveFileWriter(filestring);
++ fileReader = new cLiveFileReader(filestring, fileWriter->FileNumber());
++ }
++ }
++ free = head >= tail ? max(tail,bufSize-head) : tail - head;
++ }
++}
++
++void cLiveBuffer::Action(void)
++{
++ while (Running()) {
++ int Count;
++ Lock();
++ uchar *p = remux->Get(Count, &pictureType);
++ if (p) {
++ Store(p,Count);
++ remux->Del(Count);
++ }
++ Unlock();
++ if (writeHD) {
++ int free = head >= tail ? max(tail,bufSize-head) : tail - head;
++ if (free < 2*MAXFRAMESIZE)
++ Write(false);
++ }
++ }
++
++}
++
++void cLiveBuffer::SetNewRemux(cRemux *Remux, const cChannel *Channel)
++{
++ if (Channel != channel) {
++ index->Switched();
++ }
++ channel = Channel;
++ if (!Remux) {
++ Cancel(3);
++ return;
++ }
++ if (fileReader) fileReader->Clear();
++ if (!Running() && Remux) {
++ remux = Remux;
++ Start();
++ }
++ else {
++ Cancel(-1);
++ Lock();
++ if (writeHD && Setup.InRAM && number == 0 && !status) {
++ index->Clear();
++ delete fileReader; fileReader = NULL;
++ delete fileWriter; fileWriter = NULL;
++ writeHD = false;
++ }
++ remux = Remux;
++ Unlock();
++ Start();
++ }
++}
++
++int cLiveBuffer::GetFrame(uchar **Buffer, int Number, int Off)
++{
++ if (!Buffer) {
++ if (fileReader) fileReader->Clear();
++ return -1;
++ }
++ if (Number < FirstIndex())
++ return -2;
++ if (!index->HasFrame(Number) && (Off < 0 || !index->HasFrame(Number-1)))
++ return -1;
++ lastRead = Number;
++ if (!index->IsWritten(Number)) {
++ index->RwLock->Lock(false);
++ int size=index->Size(Number);
++ int off = index->GetOffset(Number);
++ if (off >= 0) {
++ if (Off>=0) {
++ size -= Off;
++ off += Off;
++ }
++ if (size > 0) {
++ uchar *b = MALLOC(uchar, size);
++ *Buffer = b;
++ memcpy(b,&buffer[off],size);
++ }
++ if (!index->HasFrame(Number))
++ size = -size;
++ }
++ else
++ size = -1;
++ index->RwLock->Unlock();
++ return size;
++ }
++ else
++ return fileReader->Read(Buffer,index->GetOffset(Number),index->Size(Number));
++ return -1;
++}
++
++void cLiveBuffer::CreateIndexFile(const char *FileName, int64_t PTS, int EndFrame)
++{
++ if (liveCutter) {
++ if (liveCutter->Active()) {
++ startFrame = -1;
++ return;
++ }
++ else {
++ delete liveCutter;
++ liveCutter = NULL;
++ }
++ }
++ if (startFrame < 0)
++ return;
++ cIndexFile *indexFile = new cIndexFile(FileName, true);
++ if (!indexFile)
++ return;
++ int endFrame = EndFrame ? EndFrame : index->FindIFrame(PTS, buffer);
++ int timeout = 0;
++ while (endFrame < 0 && timeout < 50) {
++ cCondWait::SleepMs(100);
++ endFrame = index->FindIFrame(PTS, buffer);
++ timeout++;
++ }
++
++ char *data="LiveBuffer";
++ cFileName fileName(FileName, true);
++ cUnbufferedFile *file = fileName.Open();
++ file->Write(data,10);
++ int Number = fileName.Number();
++ int FileOffset=0;
++ for (int Frame = startFrame; Frame < endFrame; Frame++) {
++ uchar pt;
++ int size = index->Size(Frame, &pt);
++ if (pt == I_FRAME && FileOffset > MEGABYTE(Setup.MaxVideoFileSize)) {
++ file = fileName.NextFile();
++ file->Write(data,10);
++ FileOffset=0;
++ }
++ indexFile->Write(pt,fileName.Number(),FileOffset);
++ FileOffset+=size;
++ }
++ liveCutter = new cLiveCutterThread(FileName,Number,this,startFrame,endFrame);
++ startFrame = -1;
++ delete indexFile;
++}
++
++// --- cLiveReceiver ---------------------------------------------------------
++
++cLiveReceiver::cLiveReceiver(const cChannel *Channel)
++:cReceiver(Channel->GetChannelID(), -1, Channel->Vpid(), Channel->Apids(), Setup.UseDolbyDigital ? Channel->Dpids() : NULL, Channel->Spids())
++{
++ channel = Channel;
++ vpid = Channel->Vpid();
++ for (int i = 0; i < MAXAPIDS; i++)
++ apids[i]=Channel->Apid(i);
++ for (int i = 0; i < MAXDPIDS; i++)
++ dpids[i]= Setup.UseDolbyDigital ? Channel->Dpid(i) : 0;
++ for (int i = 0; i < MAXSPIDS; i++)
++ spids[i]=Channel->Spid(i);
++ ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, TS_SIZE * 2, true, "Recorder");
++ ringBuffer->SetTimeouts(0, 20);
++ remux = new cRemux(Channel->Vpid(), Channel->Apids(), Setup.UseDolbyDigital ? Channel->Dpids() : NULL, Channel->Spids());
++ remux->SetTimeouts(0, 50);
++ attached = false;
++}
++
++cLiveReceiver::~cLiveReceiver()
++{
++ Detach();
++ Cancel(3);
++ delete remux;
++ delete ringBuffer;
++}
++
++void cLiveReceiver::Activate(bool On)
++{
++ if (On) {
++ Start();
++ attached = true;
++ }
++ else {
++ Cancel(-1);
++ attached = false;
++ }
++}
++
++void cLiveReceiver::Receive(uchar *Data, int Length)
++{
++ int p = ringBuffer->Put(Data, Length);
++ if (p != Length && Running())
++ ringBuffer->ReportOverflow(Length - p);
++}
++
++void cLiveReceiver::Action(void)
++{
++ while (Running()) {
++ int r;
++ uchar *b = ringBuffer->Get(r);
++ if (b) {
++ int Count = remux->Put(b, r);
++ if (Count)
++ ringBuffer->Del(Count);
++ else
++ cCondWait::SleepMs(20);
++ }
++ }
++}
++
++bool cLiveReceiver::IsReceiving(const cChannel *Channel)
++{
++ if (vpid != Channel->Vpid())
++ return false;
++ for (int i = 0; i < MAXAPIDS; i++)
++ if (apids[i] != Channel->Apid(i))
++ return false;
++ for (int i = 0; i < MAXDPIDS && Setup.UseDolbyDigital; i++)
++ if (dpids[i] != Channel->Dpid(i))
++ return false;
++ for (int i = 0; i < MAXSPIDS; i++)
++ if (spids[i] != Channel->Spid(i))
++ return false;
++
++ return true;
++}
++
++// --- cLiveBackTrace --------------------------------------------------------
++
++#define AVG_FRAME_SIZE 15000
++#define DVB_BUF_SIZE (256 * 1024)
++#define BACKTRACE_ENTRIES (DVB_BUF_SIZE / AVG_FRAME_SIZE + 20)
++
++class cLiveBackTrace {
++private:
++ int index[BACKTRACE_ENTRIES];
++ int length[BACKTRACE_ENTRIES];
++ int pos, num;
++public:
++ cLiveBackTrace(void);
++ void Clear(void);
++ void Add(int Index, int Length);
++ int Get(bool Forward);
++ };
++
++cLiveBackTrace::cLiveBackTrace(void)
++{
++ Clear();
++}
++
++void cLiveBackTrace::Clear(void)
++{
++ pos = num = 0;
++}
++
++void cLiveBackTrace::Add(int Index, int Length)
++{
++ index[pos] = Index;
++ length[pos] = Length;
++ if (++pos >= BACKTRACE_ENTRIES)
++ pos = 0;
++ if (num < BACKTRACE_ENTRIES)
++ num++;
++}
++
++int cLiveBackTrace::Get(bool Forward)
++{
++ int p = pos;
++ int n = num;
++ int l = DVB_BUF_SIZE + (Forward ? 0 : 256 * 1024);
++ int i = -1;
++
++ while (n && l > 0) {
++ if (--p < 0)
++ p = BACKTRACE_ENTRIES - 1;
++ i = index[p] - 1;
++ l -= length[p];
++ n--;
++ }
++ return i;
++}
++
++// --- cLivePlayer -----------------------------------------------------------
++
++#define PLAYERBUFSIZE MEGABYTE(1)
++
++#define MAX_VIDEO_SLOWMOTION 63
++#define NORMAL_SPEED 4
++#define MAX_SPEEDS 3
++#define SPEED_MULT 12
++int cLivePlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
++
++cLivePlayer::cLivePlayer(cLiveBuffer *LiveBuffer)
++:cThread("liveplayer")
++{
++ liveBuffer = LiveBuffer;
++ ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
++ readIndex = writeIndex = -1;
++ readFrame = playFrame = NULL;
++ firstPacket = true;
++ playMode = pmPlay;
++ playDir = pdForward;
++ trickSpeed = NORMAL_SPEED;
++ Off = 0;
++ backTrace = new cLiveBackTrace;
++ liveBuffer->SetStatus(0);
++}
++
++cLivePlayer::~cLivePlayer()
++{
++ liveBuffer->SetStatus(-2);
++ Detach();
++ Cancel(3);
++ liveBuffer->GetFrame(NULL,0);
++ delete readFrame;
++ delete backTrace;
++ delete ringBuffer;
++}
++
++void cLivePlayer::TrickSpeed(int Increment)
++{
++ int nts = trickSpeed + Increment;
++ if (Speeds[nts] == 1) {
++ trickSpeed = nts;
++ if (playMode == pmFast)
++ Play();
++ else
++ Pause();
++ }
++ else if (Speeds[nts]) {
++ trickSpeed = nts;
++ int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
++ int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
++ if (sp > MAX_VIDEO_SLOWMOTION)
++ sp = MAX_VIDEO_SLOWMOTION;
++ DeviceTrickSpeed(sp);
++ }
++}
++
++void cLivePlayer::Empty(void)
++{
++ LOCK_THREAD;
++ liveBuffer->GetFrame(NULL,0);
++ if ((readIndex = backTrace->Get(playDir == pdForward)) < 0)
++ readIndex = writeIndex;
++ delete readFrame;
++ readFrame = NULL;
++ playFrame = NULL;
++ ringBuffer->Clear();
++ DeviceClear();
++ backTrace->Clear();
++ firstPacket = true;
++ Off = 0;
++}
++
++void cLivePlayer::Activate(bool On)
++{
++ if (On)
++ Start();
++ else
++ Cancel(-1);
++}
++
++void cLivePlayer::Action(void)
++{
++ int PollTimeouts = 0;
++ uchar *b = NULL;
++ uchar **pb = &b;
++ uchar *p = NULL;
++ int pc = 0;
++
++ if (liveBuffer->Stay())
++ readIndex = liveBuffer->LastRead();
++ else
++ readIndex = liveBuffer->LastIndex();
++
++ bool Sleep = false;
++ bool WaitingForData = false;
++
++ while (Running()) {
++ if (Sleep) {
++ if (WaitingForData)
++ cCondWait::SleepMs(3);
++ else
++ cCondWait::SleepMs(3);
++ Sleep = false;
++ }
++
++ cPoller Poller;
++ if (!DevicePoll(Poller, 100)) {
++ PollTimeouts++;
++ continue;
++ }
++ else
++ PollTimeouts = 0;
++
++ if (PollTimeouts >= 6) {
++ DeviceClear();
++ PlayPes(NULL, 0);
++ }
++
++ LOCK_THREAD;
++
++ if (playMode != pmStill && playMode != pmPause) {
++ if (!readFrame) {
++ if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
++ int Index = liveBuffer->GetNextIFrame(readIndex, playDir == pdForward);
++ if (Index < 0) {
++ if (!DeviceFlush(100))
++ continue;
++ DevicePlay();
++ playMode = pmPlay;
++ playDir = pdForward;
++ continue;
++ }
++ int r = liveBuffer->GetFrame(pb, Index);
++ if (r>0) {
++ readIndex = Index;
++ WaitingForData = false;
++ readFrame = new cFrame(b, -r, ftUnknown, readIndex);
++ b = NULL;
++ }
++ else
++ WaitingForData = true;
++ }
++ else {
++ int r=liveBuffer->GetFrame(pb, readIndex+1, Off);
++ if (r>0)
++ readIndex++;
++ if (r > 0 || r < -10) {
++ WaitingForData = false;
++ readFrame = new cFrame(b, -abs(r), ftUnknown, readIndex);
++ b = NULL;
++ if (r<0)
++ Off += -r;
++ else
++ Off = 0;
++ }
++ else if (r==-2)
++ readIndex = liveBuffer->GetNextIFrame(liveBuffer->FirstIndex(), true)-1;
++ else
++ WaitingForData = true;
++ }
++ }
++ if (readFrame) {
++ if (ringBuffer->Put(readFrame))
++ readFrame = NULL;
++ }
++ }
++ else
++ Sleep = true;
++ if (!playFrame) {
++ playFrame = ringBuffer->Get();
++ p = NULL;
++ pc = 0;
++ }
++ if (playFrame) {
++ if (!p) {
++ p = playFrame->Data();
++ pc = playFrame->Count();
++ if (p) {
++ if (firstPacket) {
++ PlayPes(NULL, 0);
++ cRemux::SetBrokenLink(p, pc);
++ firstPacket = false;
++ }
++ }
++ }
++ if (p) {
++ int w = PlayPes(p, pc, playMode != pmPlay);
++ if (w > 0) {
++ p += w;
++ pc -= w;
++ }
++ else {
++ if (w < 0 && FATALERRNO) {
++ LOG_ERROR;
++ break;
++ }
++ }
++ }
++ if (pc == 0) {
++ writeIndex = playFrame->Index();
++ backTrace->Add(playFrame->Index(), playFrame->Count());
++ ringBuffer->Drop(playFrame);
++ playFrame = NULL;
++ p = NULL;
++ }
++ }
++ else
++ Sleep = true;
++ }
++}
++
++void cLivePlayer::Pause(void)
++{
++ if (playMode == pmPause || playMode == pmStill)
++ Play();
++ else {
++ liveBuffer->SetStatus(1);
++ LOCK_THREAD;
++ if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
++ Empty();
++ DeviceFreeze();
++ playMode = pmPause;
++ }
++}
++
++void cLivePlayer::Play(void)
++{
++ if (playMode != pmPlay) {
++ liveBuffer->SetStatus(0);
++ LOCK_THREAD;
++ if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
++ Empty();
++ DevicePlay();
++ playMode = pmPlay;
++ playDir = pdForward;
++ }
++}
++
++void cLivePlayer::Forward(void)
++{
++ switch (playMode) {
++ case pmFast:
++ if (Setup.MultiSpeedMode) {
++ TrickSpeed(playDir == pdForward ? 1 : -1);
++ break;
++ }
++ else if (playDir == pdForward) {
++ Play();
++ break;
++ }
++ case pmPlay: {
++ LOCK_THREAD;
++ Empty();
++ DeviceMute();
++ playMode = pmFast;
++ playDir = pdForward;
++ trickSpeed = NORMAL_SPEED;
++ TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
++ }
++ break;
++ case pmSlow:
++ if (Setup.MultiSpeedMode) {
++ TrickSpeed(playDir == pdForward ? -1 : 1);
++ break;
++ }
++ else if (playDir == pdForward) {
++ Pause();
++ break;
++ }
++ case pmStill:
++ case pmPause:
++ DeviceMute();
++ playMode = pmSlow;
++ playDir = pdForward;
++ trickSpeed = NORMAL_SPEED;
++ TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
++ break;
++ }
++}
++
++void cLivePlayer::Backward(void)
++{
++ switch (playMode) {
++ case pmFast:
++ if (Setup.MultiSpeedMode) {
++ TrickSpeed(playDir == pdBackward ? 1 : -1);
++ break;
++ }
++ else if (playDir == pdBackward) {
++ Play();
++ break;
++ }
++ case pmPlay: {
++ LOCK_THREAD;
++ Empty();
++ DeviceMute();
++ playMode = pmFast;
++ playDir = pdBackward;
++ trickSpeed = NORMAL_SPEED;
++ TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
++ }
++ break;
++ case pmSlow:
++ if (Setup.MultiSpeedMode) {
++ TrickSpeed(playDir == pdBackward ? -1 : 1);
++ break;
++ }
++ else if (playDir == pdBackward) {
++ Pause();
++ break;
++ }
++ case pmStill:
++ case pmPause: {
++ LOCK_THREAD;
++ Empty();
++ DeviceMute();
++ playMode = pmSlow;
++ playDir = pdBackward;
++ trickSpeed = NORMAL_SPEED;
++ TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
++ }
++ break;
++ }
++}
++
++bool cLivePlayer::Stop(void)
++{
++ liveBuffer->SetStatus(-1);
++ if (readIndex >= liveBuffer->LastIndex() - 25)
++ return false;
++ LOCK_THREAD;
++ Empty();
++ readIndex = writeIndex = liveBuffer->GetNextIFrame(liveBuffer->LastIndex()-1, false)-1;
++ Play();
++ return true;
++}
++
++void cLivePlayer::SkipSeconds(int Seconds)
++{
++ if (Seconds) {
++ LOCK_THREAD;
++ Empty();
++ int Index = writeIndex;
++ if (Index >= 0) {
++ Index = max(Index + Seconds * FRAMESPERSEC, liveBuffer->FirstIndex());
++ if (Index > liveBuffer->FirstIndex())
++ Index = liveBuffer->GetNextIFrame(Index, false);
++ if (Index >= 0)
++ readIndex = writeIndex = Index - 1;
++ }
++ Play();
++ }
++}
++
++bool cLivePlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
++{
++ if (liveBuffer) {
++ if (playMode == pmStill)
++ Current = max(readIndex, 0);
++ else {
++ Current = max(writeIndex, 0);
++ // TODO SnapToIFrame
++ }
++ Total = liveBuffer->LastIndex() - liveBuffer->FirstIndex();
++ Current -= liveBuffer->FirstIndex();
++ return true;
++ }
++ Current = Total = -1;
++ return false;
++}
++
++bool cLivePlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
++{
++ Play = (playMode == pmPlay || playMode == pmFast);
++ Forward = (playDir == pdForward);
++ if (playMode == pmFast || playMode == pmSlow)
++ Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0 ;
++ else
++ Speed = -1;
++ return true;
++}
++
++// --- cLiveBufferControl ----------------------------------------------------
++
++cLiveBufferControl::cLiveBufferControl(cLivePlayer *Player)
++:cControl(Player, true)
++{
++ player = Player;
++ displayReplay = NULL;
++ visible = modeOnly = shown = false;
++ lastCurrent = lastTotal = -1;
++ lastPlay = lastForward = false;
++ lastSpeed = -2;
++ timeoutShow = 0;
++}
++
++cLiveBufferControl::~cLiveBufferControl()
++{
++ Hide();
++ cTransferControl::receiverDevice = NULL;
++ cLiveBufferManager::livePlayer = NULL;
++ delete player;
++ cLiveBufferManager::liveControl = NULL;
++ if (!Setup.LiveBuffer)
++ cLiveBufferManager::Shutdown();
++}
++
++bool cLiveBufferControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
++{
++ if (player) {
++ player->GetIndex(Current, Total, SnapToIFrame);
++ return true;
++ }
++ return false;
++
++}
++
++bool cLiveBufferControl::GetReplayMode(bool &Play, bool &Forward, int &Speed)
++{
++ return player && player->GetReplayMode(Play, Forward, Speed);
++}
++
++void cLiveBufferControl::ShowTimed(int Seconds)
++{
++ if (modeOnly)
++ Hide();
++ if (!visible) {
++ shown = ShowProgress(true);
++ timeoutShow = (shown && Seconds > 0) ? time(NULL) + Seconds : 0;
++ }
++}
++
++void cLiveBufferControl::Show(void)
++{
++ ShowTimed();
++}
++
++void cLiveBufferControl::Hide(void)
++{
++ if (visible) {
++ delete displayReplay;
++ displayReplay = NULL;
++ SetNeedsFastResponse(false);
++ visible = false;
++ modeOnly = false;
++ lastPlay = lastForward = false;
++ lastSpeed = -2; // an invalid value
++// timeSearchActive = false;
++ }
++}
++
++void cLiveBufferControl::ShowMode(void)
++{
++ if (visible || Setup.ShowReplayMode && !cOsd::IsOpen()) {
++ bool Play, Forward;
++ int Speed;
++ if (GetReplayMode(Play, Forward, Speed) && (!visible || Play != lastPlay || Forward != lastForward || Speed != lastSpeed)) {
++ bool NormalPlay = (Play && Speed == -1);
++
++ if (!visible) {
++ if (NormalPlay)
++ return;
++ visible = modeOnly = true;
++ displayReplay = Skins.Current()->DisplayReplay(modeOnly);
++ }
++
++ if (modeOnly && !timeoutShow && NormalPlay)
++ timeoutShow = time(NULL) + 3;
++ displayReplay->SetMode(Play, Forward, Speed);
++ lastPlay = Play;
++ lastForward = Forward;
++ lastSpeed = Speed;
++ }
++ }
++}
++
++bool cLiveBufferControl::ShowProgress(bool Initial)
++{
++ int Current, Total;
++
++ if (GetIndex(Current, Total) && Total > 0) {
++ if (!visible) {
++ displayReplay = Skins.Current()->DisplayReplay(modeOnly);
++ SetNeedsFastResponse(true);
++ visible = true;
++ }
++ if (Initial) {
++ lastCurrent = lastTotal = -1;
++ }
++ if (Total != lastTotal) {
++ displayReplay->SetTotal(IndexToHMSF(Total));
++ if (!Initial)
++ displayReplay->Flush();
++ }
++ if (Current != lastCurrent || Total != lastTotal) {
++ displayReplay->SetProgress(Current, Total);
++ if (!Initial)
++ displayReplay->Flush();
++int displayFrames = false;
++ displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames));
++ displayReplay->Flush();
++ lastCurrent = Current;
++ }
++ lastTotal = Total;
++ ShowMode();
++ return true;
++ }
++ return false;
++}
++
++eOSState cLiveBufferControl::ProcessKey(eKeys Key)
++{
++ if (visible) {
++ if (timeoutShow && time(NULL) > timeoutShow) {
++ Hide();
++ ShowMode();
++ timeoutShow = 0;
++ }
++ else if (modeOnly)
++ ShowMode();
++ else
++ shown = ShowProgress(!shown) || shown;
++ }
++ bool DoShowMode = true;
++ switch (Key) {
++ case kDown: if (!visible || modeOnly)
++ return osUnknown;
++ case kPause: if (player)
++ player->Pause();
++ break;
++ case kPlay: if (visible && !modeOnly) {
++ Hide();
++ DoShowMode = true;
++ }
++ else
++ Show();
++ case kUp: if (Key == kUp && (!visible || modeOnly))
++ return osUnknown;
++ if (player)
++ player->Play();
++ break;
++ case kLeft|k_Release:
++ if (!visible || modeOnly)
++ return osUnknown;
++ case kFastRew|k_Release:
++ if (Setup.MultiSpeedMode) break;
++ case kLeft: if (Key == kLeft && (!visible || modeOnly))
++ return osUnknown;
++ case kFastRew: if (player)
++ player->Backward();
++ break;
++ case kRight|k_Release:
++ if (!visible || modeOnly)
++ return osUnknown;
++ case kFastFwd|k_Release:
++ if (Setup.MultiSpeedMode) break;
++ case kRight: if (Key == kRight && (!visible || modeOnly))
++ return osUnknown;
++ case kFastFwd: if (player)
++ player->Forward();
++ break;
++ case kGreen|k_Repeat:
++ case kGreen: if (visible && !modeOnly && player)
++ player->SkipSeconds(-60);
++ else
++ return osUnknown;
++ break;
++ case kYellow|k_Repeat:
++ case kYellow: if (visible && !modeOnly && player)
++ player->SkipSeconds(60);
++ else
++ return osUnknown;
++ break;
++ default: {
++ DoShowMode = false;
++ switch (Key) {
++ case kStop: if (player)
++ if (player->Stop())
++ break;
++ return osUnknown;
++ case kBack: if (visible && !modeOnly && player)
++ Hide();
++ else
++ return osUnknown;
++ break;
++ case kRecord: {
++ cTimer *timer = new cTimer(true);
++ int Current, Total;
++ GetIndex(Current,Total);
++ time_t start = time(NULL) - (Total - Current) / FRAMESPERSEC;
++ time_t t = start;
++ struct tm tm_r;
++ struct tm *now = localtime_r(&t, &tm_r);
++ timer->start = now->tm_hour * 100 + now->tm_min;
++ timer->stop = now->tm_hour * 60 + now->tm_min + Setup.InstantRecordTime;
++ timer->stop = (timer->stop / 60) * 100 + (timer->stop % 60);
++ if (timer->stop >= 2400)
++ timer->stop -= 2400;
++ timer->day = cTimer::SetTime(start, 0);
++ timer->channel = Channels.GetByNumber(cDevice::CurrentChannel());
++ Timers.Add(timer);
++ Timers.SetModified();
++ if (cRecordControls::Start(timer))
++ Skins.Message(mtInfo, tr("Recording started"));
++ break;
++ }
++ default: return osUnknown;
++ }
++ }
++ }
++ if (DoShowMode)
++ ShowMode();
++ return osContinue;
++}
++
++// --- cLiveBufferManager ----------------------------------------------------
++
++cLiveReceiver *cLiveBufferManager::liveReceiver[MAXLIVEBUFFERS] = { NULL };
++cLiveBuffer *cLiveBufferManager::liveBuffer[MAXLIVEBUFFERS] = { NULL };
++cDevice *cLiveBufferManager::receiverDevice[MAXLIVEBUFFERS] = { NULL };
++cLivePlayer *cLiveBufferManager::livePlayer = NULL;
++cLiveBufferControl *cLiveBufferManager::liveControl = NULL;
++
++void cLiveBufferManager::ChannelSwitch(cDevice *ReceiverDevice, const cChannel *Channel)
++{
++ int i;
++ for (i=0; i<MAXLIVEBUFFERS; i++)
++ if (liveBuffer[i] && liveBuffer[i]->Channel() == Channel)
++ break;
++ if (i == MAXLIVEBUFFERS) {
++ if (Setup.InRAM && !liveBuffer[0])
++ i = 0;
++ else
++ for (i=0; i<MAXLIVEBUFFERS; i++)
++ if (liveBuffer[i] && (!liveBuffer[i]->Stay() || !liveReceiver[i]->IsAttached()))
++ break;
++ if (i == MAXLIVEBUFFERS) {
++ for (i = 0; i<MAXLIVEBUFFERS; i++)
++ if (!liveBuffer[i])
++ break;
++ }
++ }
++ if (liveBuffer[i]) {
++ if (!(liveReceiver[i]->IsReceiving(Channel) && liveReceiver[i]->IsAttached())) {
++ cLiveReceiver *oldReceiver = liveReceiver[i];
++ liveReceiver[i] = new cLiveReceiver(Channel);
++ ReceiverDevice->AttachReceiver(liveReceiver[i]);
++ liveBuffer[i]->SetNewRemux(liveReceiver[i]->remux, Channel);
++ delete oldReceiver;
++ receiverDevice[i] = ReceiverDevice;
++ }
++ else if (liveBuffer[i]->Stay())
++ liveBuffer[i]->SetStatus(2);
++ }
++ else {
++ liveReceiver[i] = new cLiveReceiver(Channel);
++ ReceiverDevice->AttachReceiver(liveReceiver[i]);
++ receiverDevice[i] = ReceiverDevice;
++ char FileName[256];
++ sprintf(FileName,"%s/LiveBuffer",BufferDirectory);
++ liveBuffer[i] = new cLiveBuffer(FileName,liveReceiver[i]->remux, Channel, i);
++ }
++ delete liveControl;
++
++ cTransferControl::receiverDevice = receiverDevice[i];
++
++ livePlayer = new cLivePlayer(liveBuffer[i]);
++ cControl::Launch(liveControl = new cLiveBufferControl(livePlayer));
++
++ for (int j = 0; j < MAXLIVEBUFFERS; j++)
++ if (i!=j && liveBuffer[j] && !liveBuffer[j]->LiveCutterActive() && (!liveBuffer[j]->Stay() || !liveReceiver[j]->IsAttached())) {
++ delete liveBuffer[j];
++ liveBuffer[j] = NULL;
++ delete liveReceiver[j];
++ liveReceiver[j] = NULL;
++ }
++}
++
++void cLiveBufferManager::Shutdown(void)
++{
++ delete liveControl;
++ for (int i = 0; i < MAXLIVEBUFFERS; i++) {
++ delete liveBuffer[i];
++ liveBuffer[i] = NULL;
++ delete liveReceiver[i];
++ liveReceiver[i] = NULL;
++ }
++ char FileName[256];
++ sprintf(FileName,"%s/LiveBuffer",BufferDirectory);
++ RemoveFileOrDir(FileName, true);
++}
++
++cLiveBuffer *cLiveBufferManager::InLiveBuffer(cTimer *timer, int *StartFrame, int *EndFrame)
++{
++ bool recstart = false;
++ if (timer && timer->HasFlags(tfRecording)) {
++ timer->SetFlags(tfhasLiveBuf);
++ recstart = true;
++ }
++ for (int i = 0; i < MAXLIVEBUFFERS; i++)
++ if (timer && timer->HasFlags(tfActive) && !timer->HasFlags(tfVps) && (!timer->HasFlags(tfhasLiveBuf) || recstart) && liveBuffer[i] && liveReceiver[i]->GetChannel() == timer->Channel()) {
++ int now = liveBuffer[i]->LastIndex();
++ int first = liveBuffer[i]->FirstIndex() + 50;
++ if (now-first < 250) // Erst wenn LiveBuffer größer 10s
++ return NULL;
++ if (timer->StartTime() < time(NULL) && now-first > (time(NULL)-timer->StopTime())*FRAMESPERSEC) {
++ if (StartFrame) {
++ if ((time(NULL)-timer->StartTime())*FRAMESPERSEC < now - first)
++ *StartFrame = now - (time(NULL) - timer->StartTime())*FRAMESPERSEC;
++ else
++ *StartFrame = first;
++ }
++ if (EndFrame) {
++ if (time(NULL) > timer->StopTime())
++ *EndFrame = now - (time(NULL) - timer->StopTime()) * FRAMESPERSEC;
++ else
++ *EndFrame = 0;
++ }
++ return liveBuffer[i];
++ }
++ }
++ return NULL;
++}
++
++int cLiveBufferManager::Impact(cDevice *device, const cChannel *Channel, bool LiveView)
++{
++ if (!Setup.LiveBuffer)
++ return 0;
++ int r = 1;
++ for (int i=0; i < MAXLIVEBUFFERS; i++)
++ if (liveBuffer[i] && receiverDevice[i] == device) {
++ if (liveBuffer[i]->Stay() && liveReceiver[i]->IsAttached())
++ if (liveBuffer[i]->Channel()->Transponder() != Channel->Transponder() ||
++ liveBuffer[i]->Channel()->Source() != Channel->Source())
++ r = 2;
++ }
++ return r;
++}
++
++bool cLiveBufferManager::AllowsChannelSwitch(void)
++{
++ return true; // return !liveBuffer || !liveBuffer->LiveCutterActive();
++}
++#endif /* LIVEBUFFER */
+diff -ruNp vdr-1.6.0-2/livebuffer.h vdr-1.6.0-2-extensions/livebuffer.h
+--- vdr-1.6.0-2/livebuffer.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/livebuffer.h 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,301 @@
++#ifndef __LIVEBUFFER_H
++#define __LIVEBUFFER_H
++
++#include "player.h"
++#include "receiver.h"
++#include "remux.h"
++#include "ringbuffer.h"
++#include "thread.h"
++
++#define LIVEBUFSIZE MEGABYTE(5)
++
++#define INDEXBLOCKSIZE 20000
++
++class cUnbufferedFile64 {
++private:
++ int fd;
++ off64_t curpos;
++ off64_t cachedstart;
++ off64_t cachedend;
++ off64_t begin;
++ off64_t lastpos;
++ off64_t ahead;
++ size_t readahead;
++ size_t written;
++ size_t totwritten;
++ int FadviseDrop(off64_t Offset, off64_t Len);
++public:
++ cUnbufferedFile64(void);
++ ~cUnbufferedFile64();
++ int Open(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
++ int Close(void);
++ off64_t Seek(off64_t Offset, int Whence);
++ ssize_t Read(void *Data, size_t Size);
++ ssize_t Write(const void *Data, size_t Size);
++ static cUnbufferedFile64 *Create(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
++ };
++
++class cFileName64 {
++private:
++ cUnbufferedFile64 *file;
++ int fileNumber;
++ char *fileName, *pFileNumber;
++ bool record;
++ bool blocking;
++public:
++ cFileName64(const char *FileName, bool Record, bool Blocking = false);
++ ~cFileName64();
++ const char *Name(void) { return fileName; }
++ int Number(void) { return fileNumber; }
++ void SetNumber(int Number);
++ cUnbufferedFile64 *Open(void);
++ void Close(void);
++ void CloseAndRemove(void);
++ void Remove(void);
++ };
++
++class cLiveIndex {
++friend class cLiveCutterThread;
++friend class cLiveBuffer;
++private:
++ struct frame {
++ off64_t offset ; //:36;
++ bool written ; //:1 ;
++ uchar pt ; //:3 ;
++ };
++ struct block {
++ frame Frame[INDEXBLOCKSIZE];
++ block *previous, *next;
++ };
++ block *headblock, *tailblock;
++ int blockCount;
++ int head, tail;
++ int blank;
++ int start;
++ int writtenCount, delCount;
++ int DPos, DCount;
++ int lastWrittenSize, lastCount;
++ off64_t fileSize;
++ int bufSize;
++ int lastFrame;
++ int GetFrame(block** Block, int Number);
++ cRwLock *RwLock;
++public:
++ cLiveIndex(int BufSize);
++ ~cLiveIndex();
++ void Add(uchar pt, int pos);
++ void AddData(int pos);
++ void Delete(off64_t FilePos);
++ int DelFirst(void);
++ void Clear(void);
++ void SetBlank(int Blank) { RwLock->Lock(true); blank = Blank; RwLock->Unlock(); }
++ int NextWrite();
++ void WrittenTo(off64_t FilePos, int Count);
++ bool HasFrame(int Number) { return lastFrame > Number && Number >=delCount; }
++ bool IsWritten(int Number) { return writtenCount > Number; }
++ off64_t GetOffset(int Number);
++ int Size(int Number, uchar *PictureType = NULL);
++ int GetNextIFrame(int Index, bool Forward);
++ int FindIFrame(int64_t PTS, uchar *Buffer);
++ void Switched();
++ int Last(void) { return lastFrame+1; }
++ int First(void) { return start > delCount ? start : delCount; }
++};
++
++class cLiveFileReader : public cThread {
++private:
++ cFileName64 *fileName;
++ cUnbufferedFile64 *readFile;
++ cCondWait newSet;
++ off64_t filePos;
++ int hasData, length, wanted;
++ uchar *buffer;
++protected:
++ virtual void Action(void);
++public:
++ cLiveFileReader(const char *FileName, int Number);
++ ~cLiveFileReader();
++ int Read(uchar **Buffer, off64_t FilePos, int Size);
++ void Clear(void);
++};
++
++class cLiveFileWriter : public cThread {
++private:
++ cFileName64 *fileName;
++ cUnbufferedFile64 *writeFile;
++ cCondWait newSet,written;
++ off64_t filePos,fileSize;
++ int hasWritten, length, wantWrite;
++ uchar *buffer;
++ time_t lastCheck;
++ bool full;
++ bool LowDiskSpace(void);
++protected:
++ virtual void Action(void);
++public:
++ cLiveFileWriter(const char *FileName);
++ ~cLiveFileWriter();
++ off64_t Write(uchar *Buffer, int Size, bool Wait);
++ int FileNumber(void) { return fileName->Number(); }
++ void Clear(void);
++ bool Full(void) { return full; }
++};
++
++class cLiveBuffer;
++
++class cLiveCutterThread : public cThread {
++private:
++ cFileName64 *readFileName, *writeFileName;
++ cUnbufferedFile64 *readFile, *writeFile;
++ cLiveBuffer *liveBuffer;
++ int startFrame, endFrame;
++protected:
++ virtual void Action(void);
++public:
++ cLiveCutterThread(const char *FileName, int FileNumber, cLiveBuffer *LiveBuffer, int StartFrame, int EndFrame);
++ ~cLiveCutterThread();
++};
++
++class cLiveBuffer : public cThread {
++friend class cLiveCutterThread;
++private:
++ int number;
++ uchar *buffer;
++ int bufSize;
++ bool writeHD;
++ int lastRead;
++ int head, tail, blank;
++ cLiveIndex *index;
++ cRemux *remux;
++ cLiveFileWriter *fileWriter;
++ char *filestring;
++ uchar pictureType;
++ cLiveFileReader *fileReader;
++ int startFrame;
++ cLiveCutterThread *liveCutter;
++ const cChannel *channel;
++ int status;
++ void Write(bool Wait);
++ void Store(uchar *Data, int Count);
++protected:
++ virtual void Action(void);
++public:
++ cLiveBuffer(const char *FileName, cRemux *Remux, const cChannel *Channel, int Number);
++ virtual ~cLiveBuffer();
++ void SetStatus(int s);
++ void SetNewRemux(cRemux *Remux, const cChannel *Channel);
++ int GetFrame(uchar **Buffer, int Number, int Off = -1);
++ int GetNextIFrame(int Index, bool Forward) { return index->GetNextIFrame(Index,Forward); }
++ int LastIndex(void) { return index->Last(); }
++ int FirstIndex(void) { return index->First(); }
++ int LastRead(void) { return lastRead; }
++ void CreateIndexFile(const char *FileName, int64_t PTS, int EndFrame = 0);
++ void SetStartFrame(int StartFrame) { startFrame = StartFrame; }
++ bool LiveCutterActive(void) { return liveCutter && liveCutter->Active(); }
++ const cChannel* Channel(void) { return channel; }
++ bool Stay(void) { return status>0 && Setup.KeepPaused; };
++};
++
++class cLiveBackTrace;
++
++class cLivePlayer : public cPlayer, public cThread {
++private:
++ enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
++ enum ePlayDirs { pdForward, pdBackward };
++ static int Speeds[];
++ ePlayModes playMode;
++ ePlayDirs playDir;
++ int trickSpeed;
++ cLiveBuffer *liveBuffer;
++ cRingBufferFrame *ringBuffer;
++ cLiveBackTrace *backTrace;
++ int readIndex, writeIndex;
++ cFrame *readFrame;
++ cFrame *playFrame;
++ int Off;
++ bool firstPacket;
++ void TrickSpeed(int Increment);
++ void Empty(void);
++protected:
++ virtual void Activate(bool On);
++ virtual void Action(void);
++public:
++ cLivePlayer(cLiveBuffer *LiveBuffer);
++ virtual ~cLivePlayer();
++ void Pause(void);
++ void Play(void);
++ void Forward(void);
++ void Backward(void);
++ bool Stop(void);
++ void SkipSeconds(int Seconds);
++ virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
++ virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
++};
++
++class cLiveReceiver : public cReceiver, cThread {
++friend class cLiveBufferManager;
++friend class cLiveBufferControl;
++private:
++ cRingBufferLinear *ringBuffer;
++ cRemux *remux;
++ const cChannel *channel;
++ bool attached;
++ int vpid;
++ int apids[MAXAPIDS];
++ int dpids[MAXDPIDS];
++ int spids[MAXSPIDS];
++protected:
++ virtual void Activate(bool On);
++ virtual void Receive(uchar *Data, int Length);
++ virtual void Action(void);
++public:
++ cLiveReceiver(const cChannel *Channel);
++ virtual ~cLiveReceiver();
++ const cChannel *GetChannel() { return channel; }
++ bool IsReceiving(const cChannel *Channel);
++ bool IsAttached() { return attached; }
++ };
++
++class cLiveBufferControl : public cControl {
++private:
++ cSkinDisplayReplay *displayReplay;
++ cLivePlayer *player;
++ bool visible, modeOnly, shown;
++ int lastCurrent, lastTotal;
++ bool lastPlay, lastForward;
++ int lastSpeed;
++ time_t timeoutShow;
++ void ShowTimed(int Seconds = 0);
++ void ShowMode(void);
++ bool ShowProgress(bool Initial);
++public:
++ cLiveBufferControl(cLivePlayer *Player);
++ ~cLiveBufferControl();
++ bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
++ bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
++ virtual eOSState ProcessKey(eKeys Key);
++ virtual void Show(void);
++ virtual void Hide(void);
++ bool Visible(void) { return visible; }
++ };
++
++#define MAXLIVEBUFFERS 16
++
++class cLiveBufferManager {
++friend class cLiveBufferControl;
++private:
++ static cLiveReceiver* liveReceiver[MAXLIVEBUFFERS];
++ static cLiveBuffer* liveBuffer[MAXLIVEBUFFERS];
++ static cDevice* receiverDevice[MAXLIVEBUFFERS];
++ static cLivePlayer* livePlayer;
++ static cLiveBufferControl* liveControl;
++public:
++ static void ChannelSwitch(cDevice *ReceiverDevice, const cChannel *Channel);
++ static void Shutdown(void);
++ static cLiveBufferControl *GetLiveBufferControl(void) { return liveControl; }
++ static cLiveBuffer *InLiveBuffer(cTimer *timer, int *StartFrame = NULL, int *EndFrame = NULL);
++ static int Impact(cDevice *device, const cChannel *Channel, bool LiveView);
++ static bool AllowsChannelSwitch(void);
++};
++
++#endif //__LIVEBUFFER_H
+diff -ruNp vdr-1.6.0-2/mainmenuitemsprovider.h vdr-1.6.0-2-extensions/mainmenuitemsprovider.h
+--- vdr-1.6.0-2/mainmenuitemsprovider.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/mainmenuitemsprovider.h 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,62 @@
++#ifdef USE_MENUORG
++/*
++ * vdr-menuorg - A plugin for the Linux Video Disk Recorder
++ * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net>
++ *
++ * 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 of the License, 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
++ * this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ * $Id$
++ *
++ */
++
++#ifndef __MAINMENUITEMSPROVIDER_H
++#define __MAINMENUITEMSPROVIDER_H
++
++#include <vector>
++
++class cOsdItem;
++class cOsdMenu;
++
++class IMenuItemDefinition
++{
++ public:
++ virtual ~IMenuItemDefinition() {};
++ virtual bool IsCustomOsdItem() = 0;
++ virtual bool IsPluginItem() = 0;
++ virtual bool IsSeparatorItem() = 0;
++ virtual cOsdItem* CustomOsdItem() = 0;
++ virtual const char* PluginMenuEntry() = 0;
++ virtual bool IsSelected() = 0;
++ virtual int PluginIndex() = 0;
++};
++
++typedef std::vector<IMenuItemDefinition*> MenuItemDefinitions;
++
++#define MENU_ITEMS_PROVIDER_SERVICE_ID "MenuOrgPatch-v0.4.2::MainMenuItemsProvider"
++
++class IMainMenuItemsProvider
++{
++ public:
++ virtual ~IMainMenuItemsProvider() {};
++ virtual bool IsCustomMenuAvailable() = 0;
++ virtual MenuItemDefinitions* MainMenuItems() = 0;
++ virtual void EnterRootMenu() = 0;
++ virtual void EnterSubMenu(cOsdItem* item) = 0;
++ virtual bool LeaveSubMenu() = 0;
++ virtual cOsdMenu* Execute(cOsdItem* item) = 0;
++};
++
++#endif //__MAINMENUITEMSPROVIDER_H
++#endif /* MENUORG */
+diff -ruNp vdr-1.6.0-2/Make.config.template vdr-1.6.0-2-extensions/Make.config.template
+--- vdr-1.6.0-2/Make.config.template 2008-01-13 13:54:09.000000000 +0100
++++ vdr-1.6.0-2-extensions/Make.config.template 2009-04-09 20:48:26.000000000 +0200
+@@ -41,8 +41,262 @@ RCU_DEVICE = /dev/ttyS1
+ ## Define if you want vdr to not run as root
+ #VDR_USER = vdr
+
++### VDR-Extensions:
++# Comment the patches you don't need
++# DVDCHAPJUMP needs DVDARCHIVE enabled
++# DVDARCHIVE needs LIEMIEXT enabled
++# SORTRECORDS needs LIEMIEXT enabled
++# you can only enable MENUORG or SETUP
++
++#ANALOGTV = 1
++#ATSC = 1
++#CHANNELSCAN = 1
++CMDRECCMDI18N = 1
++CMDSUBMENU = 1
++#CUTTERLIMIT = 1
++#CUTTERQUEUE = 1
++CUTTIME = 1
++DDEPGENTRY = 1
++#DELTIMESHIFTREC = 1
++DOLBYINREC = 1
++#DVBPLAYER = 1
++#DVBSETUP = 1
++#DVDARCHIVE = 1
++#DVDCHAPJUMP = 1
++#DVLFRIENDLYFNAMES = 1
++#DVLRECSCRIPTADDON = 1
++#DVLVIDPREFER = 1
++#EM84XX = 1
++#GRAPHTFT = 1
++#HARDLINKCUTTER = 1
++JUMPPLAY = 1
++LIEMIEXT = 1
++#LIRCSETTINGS = 1
++#LIVEBUFFER = 1
++#LNBSHARE = 1
++#MAINMENUHOOKS = 1
++#MENUORG = 1
++#NOEPG = 1
++#OSDMAXITEMS = 1
++#PARENTALRATING = 1
++#PINPLUGIN = 1
++PLUGINAPI = 1
++PLUGINMISSING = 1
++#PLUGINPARAM = 1
++#ROTOR = 1
++SETTIME = 1
++#SETUP = 1
++#SOFTOSD = 1
++#SOURCECAPS = 1
++#SORTRECORDS = 1
++#STREAMDEVEXT = 1
++#SYNCEARLY = 1
++#TIMERCMD = 1
++#TIMERINFO = 1
++#TTXTSUBS = 1
++#VALIDINPUT = 1
++#VOLCTRL = 1
++#WAREAGLEICON = 1
++#YAEPG = 1
++
+ ### You don't need to touch the following:
+
+ ifdef DVBDIR
+ INCLUDES += -I$(DVBDIR)/include
+ endif
++
++ifdef ANALOGTV
++DEFINES += -DUSE_ANALOGTV
++endif
++
++ifdef ATSC
++DEFINES += -DUSE_ATSC
++endif
++
++ifdef CHANNELSCAN
++DEFINES += -DUSE_CHANNELSCAN
++endif
++
++ifdef CMDRECCMDI18N
++DEFINES += -DUSE_CMDRECCMDI18N
++endif
++
++ifdef CMDSUBMENU
++DEFINES += -DUSE_CMDSUBMENU
++endif
++
++ifdef CUTTERLIMIT
++DEFINES += -DUSE_CUTTERLIMIT
++endif
++
++ifdef CUTTERQUEUE
++DEFINES += -DUSE_CUTTERQUEUE
++endif
++
++ifdef CUTTIME
++DEFINES += -DUSE_CUTTIME
++endif
++
++ifdef DDEPGENTRY
++DEFINES += -DUSE_DDEPGENTRY
++endif
++
++ifdef DELTIMESHIFTREC
++DEFINES += -DUSE_DELTIMESHIFTREC
++endif
++
++ifdef DOLBYINREC
++DEFINES += -DUSE_DOLBYINREC
++endif
++
++ifdef DVBPLAYER
++DEFINES += -DUSE_DVBPLAYER
++endif
++
++ifdef DVBSETUP
++DEFINES += -DUSE_DVBSETUP
++endif
++
++ifdef DVDARCHIVE
++ifdef LIEMIEXT
++DEFINES += -DUSE_DVDARCHIVE
++endif
++endif
++
++ifdef DVLRECSCRIPTADDON
++DEFINES += -DUSE_DVLRECSCRIPTADDON
++endif
++
++ifdef DVLVIDPREFER
++DEFINES += -DUSE_DVLVIDPREFER
++endif
++
++ifdef DVLFRIENDLYFNAMES
++DEFINES += -DUSE_DVLFRIENDLYFNAMES
++endif
++
++ifdef EM84XX
++DEFINES += -DUSE_EM84XX
++endif
++
++ifdef GRAPHTFT
++DEFINES += -DUSE_GRAPHTFT
++endif
++
++ifdef HARDLINKCUTTER
++DEFINES += -DUSE_HARDLINKCUTTER
++endif
++
++ifdef JUMPPLAY
++DEFINES += -DUSE_JUMPPLAY
++endif
++
++ifdef LIEMIEXT
++DEFINES += -DUSE_LIEMIEXT
++endif
++
++ifdef LIRCSETTINGS
++DEFINES += -DUSE_LIRCSETTINGS
++endif
++
++ifdef LIVEBUFFER
++DEFINES += -DUSE_LIVEBUFFER
++endif
++
++ifdef LNBSHARE
++DEFINES += -DUSE_LNBSHARE
++endif
++
++ifdef MAINMENUHOOKS
++DEFINES += -DUSE_MAINMENUHOOKS
++endif
++
++ifdef MENUORG
++DEFINES += -DUSE_MENUORG
++else
++ifdef SETUP
++DEFINES += -DUSE_SETUP
++endif
++endif
++
++ifdef NOEPG
++DEFINES += -DUSE_NOEPG
++endif
++
++ifdef OSDMAXITEMS
++DEFINES += -DUSE_OSDMAXITEMS
++endif
++
++ifdef PARENTALRATING
++DEFINES += -DUSE_PARENTALRATING
++endif
++
++ifdef PINPLUGIN
++DEFINES += -DUSE_PINPLUGIN
++endif
++
++ifdef PLUGINMISSING
++DEFINES += -DUSE_PLUGINMISSING
++endif
++
++ifdef PLUGINPARAM
++DEFINES += -DUSE_PLUGINPARAM
++endif
++
++ifdef ROTOR
++DEFINES += -DUSE_ROTOR
++endif
++
++ifdef SETTIME
++DEFINES += -DUSE_SETTIME
++endif
++
++ifdef SOFTOSD
++DEFINES += -DUSE_SOFTOSD
++endif
++
++ifdef SOURCECAPS
++DEFINES += -DUSE_SOURCECAPS
++endif
++
++ifdef SORTRECORDS
++ifdef LIEMIEXT
++DEFINES += -DUSE_SORTRECORDS
++endif
++endif
++
++ifdef SYNCEARLY
++DEFINES += -DUSE_SYNCEARLY
++endif
++
++ifdef STREAMDEVEXT
++DEFINES += -DUSE_STREAMDEVEXT
++endif
++
++ifdef TIMERCMD
++DEFINES += -DUSE_TIMERCMD
++endif
++
++ifdef TIMERINFO
++DEFINES += -DUSE_TIMERINFO
++endif
++
++ifdef TTXTSUBS
++DEFINES += -DUSE_TTXTSUBS
++endif
++
++ifdef VALIDINPUT
++DEFINES += -DUSE_VALIDINPUT
++endif
++
++ifdef VOLCTRL
++DEFINES += -DUSE_VOLCTRL
++endif
++
++ifdef WAREAGLEICON
++DEFINES += -DUSE_WAREAGLEICON
++endif
++
++ifdef YAEPG
++DEFINES += -DUSE_YAEPG
++endif
+diff -ruNp vdr-1.6.0-2/Makefile vdr-1.6.0-2-extensions/Makefile
+--- vdr-1.6.0-2/Makefile 2008-02-29 22:43:03.000000000 +0100
++++ vdr-1.6.0-2-extensions/Makefile 2009-04-09 20:48:26.000000000 +0200
+@@ -43,6 +43,22 @@ OBJS = audio.o channels.o ci.o config.o
+ skinclassic.o skins.o skinsttng.o sources.o spu.o status.o svdrp.o themes.o thread.o\
+ timers.o tools.o transfer.o vdr.o videodir.o
+
++ifdef WAREAGLEICON
++OBJS += iconpatch.o
++endif
++
++ifdef LIVEBUFFER
++OBJS += livebuffer.o
++endif
++
++ifdef SETUP
++OBJS += tinystr.o tinyxml.o tinyxmlerror.o tinyxmlparser.o submenu.o
++endif
++
++ifdef TTXTSUBS
++OBJS += vdrttxtsubshooks.o
++endif
++
+ ifndef NO_KBD
+ DEFINES += -DREMOTE_KBD
+ endif
+@@ -75,6 +91,14 @@ ifdef VFAT
+ DEFINES += -DVFAT
+ endif
+
++ifdef DVDARCHIVE
++ifdef DVDCHAPJUMP
++LIBS += -ldvdread
++INCLUDES += -I/usr/include/dvdread
++DEFINES += -DUSE_DVDCHAPJUMP
++endif
++endif
++
+ all: vdr i18n
+
+ # Implicit rules:
+@@ -140,6 +164,26 @@ include-dir:
+
+ # Plugins:
+
++ifdef PLUGINAPI
++DEFINES += -DUSE_PLUGINAPI
++plugins: include-dir
++ @failed="";\
++ noapiv="";\
++ for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do\
++ echo "Plugin $$i:";\
++ if ! grep -q "\$$(LIBDIR)/.*\$$(APIVERSION)" "$(PLUGINDIR)/src/$$i/Makefile" ; then\
++ sed -i -e s/VDRVERSION/APIVERSION/g $(PLUGINDIR)/src/$$i/Makefile;\
++ if ! grep -q "\$$(LIBDIR)/.*\$$(APIVERSION)" "$(PLUGINDIR)/src/$$i/Makefile" ; then\
++ echo "ERROR: plugin $$i doesn't honor APIVERSION - not compiled!";\
++ noapiv="$$noapiv $$i";\
++ continue;\
++ fi;\
++ fi;\
++ $(MAKE) -C "$(PLUGINDIR)/src/$$i" all || failed="$$failed $$i";\
++ done;\
++ if [ -n "$$noapiv" ] ; then echo; echo "*** plugins without APIVERSION:$$noapiv"; echo; fi;\
++ if [ -n "$$failed" ] ; then echo; echo "*** failed plugins:$$failed"; echo; fi
++else
+ plugins: include-dir
+ @failed="";\
+ noapiv="";\
+@@ -154,6 +198,7 @@ plugins: include-dir
+ done;\
+ if [ -n "$$noapiv" ] ; then echo; echo "*** plugins without APIVERSION:$$noapiv"; echo; fi;\
+ if [ -n "$$failed" ] ; then echo; echo "*** failed plugins:$$failed"; echo; exit 1; fi
++endif
+
+ clean-plugins:
+ @for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do $(MAKE) -C "$(PLUGINDIR)/src/$$i" clean; done
+diff -ruNp vdr-1.6.0-2/MANUAL vdr-1.6.0-2-extensions/MANUAL
+--- vdr-1.6.0-2/MANUAL 2008-02-24 11:09:17.000000000 +0100
++++ vdr-1.6.0-2-extensions/MANUAL 2009-04-09 20:48:26.000000000 +0200
+@@ -813,6 +813,30 @@ Version 1.6
+ 0 resulting in a file named 'resume.vdr', and any other
+ value resulting in 'resume.n.vdr'.
+
++ Jump&Play = no Turns playing on or off after jumping forward to the
++ next editing mark with the '9' key.
++
++ Play&Jump = no Turns automatic jumping over commercial breaks on or
++ off. This includes jumping to the first mark, if the
++ replay starts at the beginning of a recording - and
++ stopping the replay at the last mark.
++ With this setting enabled, the behaviour of the '8'
++ key during replay is changed too. It moves the actual
++ replay position not only three seconds before the
++ next "start" mark, but also before the next "end"
++ mark. This can be used to test, if the editing marks
++ are correctly positioned for a "smooth" jump over a
++ commercial break.
++
++ Pause at last mark = no
++ Turns pausing of replay at the last editing mark on or
++ off.
++
++ Reload marks = no Turns reloading of editing marks on or off. This can
++ be used if an external programme adjusts the editing
++ marks, e.g. noad in online mode. The marks are reloaded
++ in 10 seconds intervals.
++
+ Miscellaneous:
+
+ Min. event timeout = 30
+diff -ruNp vdr-1.6.0-2/menu.c vdr-1.6.0-2-extensions/menu.c
+--- vdr-1.6.0-2/menu.c 2008-03-16 12:15:28.000000000 +0100
++++ vdr-1.6.0-2-extensions/menu.c 2009-04-09 20:48:26.000000000 +0200
+@@ -8,11 +8,17 @@
+ */
+
+ #include "menu.h"
++#ifdef USE_WAREAGLEICON
++#include "iconpatch.h"
++#endif /* WAREAGLEICON */
+ #include <ctype.h>
+ #include <limits.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#ifdef USE_LIEMIEXT
++#include <math.h>
++#endif /* LIEMIEXT */
+ #include "channels.h"
+ #include "config.h"
+ #include "cutter.h"
+@@ -28,7 +34,17 @@
+ #include "themes.h"
+ #include "timers.h"
+ #include "transfer.h"
++#ifdef USE_TTXTSUBS
++#include "vdrttxtsubshooks.h"
++#endif /* TTXTSUBS */
+ #include "videodir.h"
++#ifdef USE_MENUORG
++#include "menuorgpatch.h"
++#endif /* MENUORG */
++
++#ifdef USE_CMDRECCMDI18N
++extern const char *ConfigDirectory;
++#endif /* CMDRECCMDI18N */
+
+ #define MAXWAIT4EPGINFO 3 // seconds
+ #define MODETIMEOUT 3 // seconds
+@@ -253,10 +269,16 @@ private:
+ cChannel *channel;
+ cChannel data;
+ char name[256];
++#ifdef USE_PLUGINPARAM
++ char pluginParam[256];
++#endif /* PLUGINPARAM */
+ void Setup(void);
+ public:
+ cMenuEditChannel(cChannel *Channel, bool New = false);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuEditChannel"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuEditChannel::cMenuEditChannel(cChannel *Channel, bool New)
+@@ -285,6 +307,9 @@ void cMenuEditChannel::Setup(void)
+
+ // Parameters for all types of sources:
+ strn0cpy(name, data.name, sizeof(name));
++#ifdef USE_PLUGINPARAM
++ strn0cpy(pluginParam, data.pluginParam, sizeof(pluginParam));
++#endif /* PLUGINPARAM */
+ Add(new cMenuEditStrItem( tr("Name"), name, sizeof(name)));
+ Add(new cMenuEditSrcItem( tr("Source"), &data.source));
+ Add(new cMenuEditIntItem( tr("Frequency"), &data.frequency));
+@@ -315,6 +340,9 @@ void cMenuEditChannel::Setup(void)
+ ST(" T") Add(new cMenuEditMapItem( tr("Transmission"), &data.transmission, TransmissionValues));
+ ST(" T") Add(new cMenuEditMapItem( tr("Guard"), &data.guard, GuardValues));
+ ST(" T") Add(new cMenuEditMapItem( tr("Hierarchy"), &data.hierarchy, HierarchyValues, tr("none")));
++#ifdef USE_PLUGINPARAM
++ ST("P ") Add(new cMenuEditStrItem( tr("Parameters"), pluginParam, sizeof(pluginParam), tr(FileNameChars)));
++#endif /* PLUGINPARAM */
+
+ SetCurrent(Get(current));
+ Display();
+@@ -329,9 +357,15 @@ eOSState cMenuEditChannel::ProcessKey(eK
+ if (Key == kOk) {
+ if (Channels.HasUniqueChannelID(&data, channel)) {
+ data.name = strcpyrealloc(data.name, name);
++#ifdef USE_PLUGINPARAM
++ data.pluginParam = strcpyrealloc(data.pluginParam, pluginParam);
++#endif /* PLUGINPARAM */
+ if (channel) {
+ *channel = data;
+ isyslog("edited channel %d %s", channel->Number(), *data.ToText());
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgChannelChange(channel, scMod);
++#endif /* STREAMDEVEXT */
+ state = osBack;
+ }
+ else {
+@@ -340,6 +374,9 @@ eOSState cMenuEditChannel::ProcessKey(eK
+ Channels.Add(channel);
+ Channels.ReNumber();
+ isyslog("added channel %d %s", channel->Number(), *data.ToText());
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgChannelChange(channel, scAdd);
++#endif /* STREAMDEVEXT */
+ state = osUser1;
+ }
+ Channels.SetModified(true);
+@@ -402,6 +439,16 @@ void cMenuChannelItem::Set(void)
+ if (!channel->GroupSep()) {
+ if (sortMode == csmProvider)
+ buffer = cString::sprintf("%d\t%s - %s", channel->Number(), channel->Provider(), channel->Name());
++#ifdef USE_WAREAGLEICON
++ else if (Setup.WarEagleIcons) {
++ if (channel->Vpid() == 1 || channel->Vpid() == 0)
++ buffer = cString::sprintf("%d\t%s %-30s", channel->Number(), IsLangUtf8() ? ICON_RADIO_UTF8 : ICON_RADIO, channel->Name());
++ else if (channel->Ca() == 0)
++ buffer = cString::sprintf("%d\t%s %-30s", channel->Number(), IsLangUtf8() ? ICON_TV_UTF8 : ICON_TV, channel->Name());
++ else
++ buffer = cString::sprintf("%d\t%s %-30s", channel->Number(), IsLangUtf8() ? ICON_TV_CRYPTED_UTF8 : ICON_TV_CRYPTED, channel->Name());
++ }
++#endif /* WAREAGLEICON */
+ else
+ buffer = cString::sprintf("%d\t%s", channel->Number(), channel->Name());
+ }
+@@ -432,6 +479,9 @@ public:
+ cMenuChannels(void);
+ ~cMenuChannels();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuChannels"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuChannels::cMenuChannels(void)
+@@ -561,6 +611,9 @@ eOSState cMenuChannels::Delete(void)
+ Propagate();
+ Channels.SetModified(true);
+ isyslog("channel %d deleted", DeletedChannel);
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgChannelChange(NULL, scDel);
++#endif /* STREAMDEVEXT */
+ if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) {
+ if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring())
+ Channels.SwitchTo(CurrentChannel->Number());
+@@ -586,6 +639,9 @@ void cMenuChannels::Move(int From, int T
+ Propagate();
+ Channels.SetModified(true);
+ isyslog("channel %d moved to %d", FromNumber, ToNumber);
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgChannelChange(ToChannel, scMod);
++#endif /* STREAMDEVEXT */
+ if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) {
+ if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring())
+ Channels.SwitchTo(CurrentChannel->Number());
+@@ -698,14 +754,47 @@ cMenuEditTimer::cMenuEditTimer(cTimer *T
+ data.SetFlags(tfActive);
+ channel = data.Channel()->Number();
+ Add(new cMenuEditBitItem( tr("Active"), &data.flags, tfActive));
++#ifdef USE_PINPLUGIN
++ if (cOsd::pinValid) Add(new cMenuEditChanItem(tr("Channel"), &channel));
++ else {
++ cString buf = cString::sprintf("%s\t%s", tr("Channel"), Channels.GetByNumber(channel)->Name());
++ Add(new cOsdItem(buf));
++ }
++#else
+ Add(new cMenuEditChanItem(tr("Channel"), &channel));
++#endif /* PINPLUGIN */
+ Add(new cMenuEditDateItem(tr("Day"), &data.day, &data.weekdays));
+ Add(new cMenuEditTimeItem(tr("Start"), &data.start));
+ Add(new cMenuEditTimeItem(tr("Stop"), &data.stop));
+ Add(new cMenuEditBitItem( tr("VPS"), &data.flags, tfVps));
+ Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
++#ifdef USE_PINPLUGIN
++ if (cOsd::pinValid || !data.fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&data.fskProtection));
++ else {
++ cString buf = cString::sprintf("%s\t%s", tr("Childlock"), data.fskProtection ? tr("yes") : tr("no"));
++ Add(new cOsdItem(buf));
++ }
++#endif /* PINPLUGIN */
++#ifdef USE_LIEMIEXT
++ char* p = strrchr(data.file, '~');
++ if (p) {
++ p++;
++ Utf8Strn0Cpy(name, p, sizeof(name));
++ Utf8Strn0Cpy(path, data.file, sizeof(path));
++ p = strrchr(path, '~');
++ if (p)
++ p[0] = 0;
++ }
++ else {
++ Utf8Strn0Cpy(name, data.file, sizeof(name));
++ Utf8Strn0Cpy(path, "", sizeof(path));
++ }
++ Add(new cMenuEditStrItem(tr("File"), name, sizeof(name), tr(FileNameChars)));
++ Add(new cMenuEditRecPathItem(tr("Path"), path, sizeof(path)));
++#else
+ Add(new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
++#endif /* LIEMIEXT */
+ SetFirstDayItem();
+ }
+ Timers.IncBeingEdited();
+@@ -745,6 +834,12 @@ eOSState cMenuEditTimer::ProcessKey(eKey
+ Skins.Message(mtError, tr("*** Invalid Channel ***"));
+ break;
+ }
++#ifdef USE_LIEMIEXT
++ if (strlen(path))
++ snprintf(data.file, sizeof(data.file), "%s~%s", path, name);
++ else
++ snprintf(data.file, sizeof(data.file), "%s", name);
++#endif /* LIEMIEXT */
+ if (!*data.file)
+ strcpy(data.file, data.Channel()->ShortName(true));
+ if (timer) {
+@@ -772,13 +867,37 @@ eOSState cMenuEditTimer::ProcessKey(eKey
+ return state;
+ }
+
++#ifdef USE_TIMERCMD
++// --- cMenuCommands ---------------------------------------------------------
++// declaration shifted so it can be used in cMenuTimers
++class cMenuCommands : public cOsdMenu {
++private:
++ cCommands *commands;
++ char *parameters;
++ eOSState Execute(void);
++public:
++ cMenuCommands(const char *Title, cCommands *Commands, const char *Parameters = NULL);
++ virtual ~cMenuCommands();
++ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuCommands"; }
++#endif /* GRAPHTFT - passt das so? */
++ };
++#endif /* TIMERCMD */
++
+ // --- cMenuTimerItem --------------------------------------------------------
+
+ class cMenuTimerItem : public cOsdItem {
+ private:
+ cTimer *timer;
++#ifdef USE_TIMERINFO
++ char diskStatus;
++#endif /* TIMERINFO */
+ public:
+ cMenuTimerItem(cTimer *Timer);
++#ifdef USE_TIMERINFO
++ void SetDiskStatus(char DiskStatus);
++#endif /* TIMERINFO */
+ virtual int Compare(const cListObject &ListObject) const;
+ virtual void Set(void);
+ cTimer *Timer(void) { return timer; }
+@@ -787,6 +906,9 @@ public:
+ cMenuTimerItem::cMenuTimerItem(cTimer *Timer)
+ {
+ timer = Timer;
++#ifdef USE_TIMERINFO
++ diskStatus = ' ';
++#endif /* TIMERINFO */
+ Set();
+ }
+
+@@ -812,8 +934,56 @@ void cMenuTimerItem::Set(void)
+ strftime(buffer, sizeof(buffer), "%Y%m%d", &tm_r);
+ day = buffer;
+ }
++#ifdef USE_LIEMIEXT
++ if (!Setup.ShowTimerStop) {
++#ifdef USE_TIMERINFO
++#ifdef USE_WAREAGLEICON
++ SetText(cString::sprintf("%c%s\t%d\t%s%s%s\t%02d:%02d\t%s",
++#else
++ SetText(cString::sprintf("%c%c\t%d\t%s%s%s\t%02d:%02d\t%s",
++#endif /* WAREAGLEICON */
++ diskStatus,
++#else
++#ifdef USE_WAREAGLEICON
++ SetText(cString::sprintf("%s\t%d\t%s%s%s\t%02d:%02d\t%s",
++#else
++ SetText(cString::sprintf("%c\t%d\t%s%s%s\t%02d:%02d\t%s",
++#endif /* WAREAGLEICON */
++#endif /* TIMERINFO */
++#ifdef USE_WAREAGLEICON
++ !(timer->HasFlags(tfActive)) ? " " : timer->FirstDay() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_ARROW_UTF8 : ICON_ARROW : "!" : timer->Recording() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_REC_UTF8 : ICON_REC : "#" : Setup.WarEagleIcons ? IsLangUtf8() ? ICON_CLOCK_UTF8 : ICON_CLOCK : ">",
++#else
++ !(timer->HasFlags(tfActive)) ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
++#endif /* WAREAGLEICON */
++ timer->Channel()->Number(),
++ *name,
++ *name && **name ? " " : "",
++ *day,
++ timer->Start() / 100,
++ timer->Start() % 100,
++ timer->File()));
++ }
++ else {
++#endif /* LIEMIEXT */
++#ifdef USE_TIMERINFO
++#ifdef USE_WAREAGLEICON
++ SetText(cString::sprintf("%c%s\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++#else
++ SetText(cString::sprintf("%c%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++#endif /* WAREAGLEICON */
++ diskStatus,
++#else
++#ifdef USE_WAREAGLEICON
++ SetText(cString::sprintf("%s\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++#else
+ SetText(cString::sprintf("%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++#endif /* WAREAGLEICON */
++#endif /* TIMERINFO */
++#ifdef USE_WAREAGLEICON
++ !(timer->HasFlags(tfActive)) ? " " : timer->FirstDay() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_ARROW_UTF8 : ICON_ARROW : "!" : timer->Recording() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_REC_UTF8 : ICON_REC : "#" : Setup.WarEagleIcons ? IsLangUtf8() ? ICON_CLOCK_UTF8 : ICON_CLOCK : ">",
++#else
+ !(timer->HasFlags(tfActive)) ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
++#endif /* WAREAGLEICON */
+ timer->Channel()->Number(),
+ *name,
+ *name && **name ? " " : "",
+@@ -823,8 +993,64 @@ void cMenuTimerItem::Set(void)
+ timer->Stop() / 100,
+ timer->Stop() % 100,
+ timer->File()));
++#ifdef USE_LIEMIEXT
++ }
++#endif /* LIEMIEXT */
++}
++
++#ifdef USE_TIMERINFO
++void cMenuTimerItem::SetDiskStatus(char DiskStatus)
++{
++ diskStatus = DiskStatus;
++ Set();
++}
++
++// --- cTimerEntry -----------------------------------------------------------
++
++class cTimerEntry : public cListObject {
++private:
++ cMenuTimerItem *item;
++ const cTimer *timer;
++ time_t start;
++public:
++ cTimerEntry(cMenuTimerItem *item) : item(item), timer(item->Timer()), start(timer->StartTime()) {}
++ cTimerEntry(const cTimer *timer, time_t start) : item(NULL), timer(timer), start(start) {}
++ virtual int Compare(const cListObject &ListObject) const;
++ bool active(void) const { return timer->HasFlags(tfActive); }
++ time_t startTime(void) const { return start; }
++ int priority(void) const { return timer->Priority(); }
++ int duration(void) const;
++ bool repTimer(void) const { return !timer->IsSingleEvent(); }
++ bool isDummy(void) const { return item == NULL; }
++ const cTimer *Timer(void) const { return timer; }
++ void SetDiskStatus(char DiskStatus);
++ };
++
++int cTimerEntry::Compare(const cListObject &ListObject) const
++{
++ cTimerEntry *entry = (cTimerEntry *)&ListObject;
++ int r = startTime() - entry->startTime();
++ if (r == 0)
++ r = entry->priority() - priority();
++ return r;
+ }
+
++int cTimerEntry::duration(void) const
++{
++ int dur = (timer->Stop() / 100 * 60 + timer->Stop() % 100) -
++ (timer->Start() / 100 * 60 + timer->Start() % 100);
++ if (dur < 0)
++ dur += 24 * 60;
++ return dur;
++}
++
++void cTimerEntry::SetDiskStatus(char DiskStatus)
++{
++ if (item)
++ item->SetDiskStatus(DiskStatus);
++}
++#endif /* TIMERINFO */
++
+ // --- cMenuTimers -----------------------------------------------------------
+
+ class cMenuTimers : public cOsdMenu {
+@@ -837,14 +1063,31 @@ private:
+ eOSState Info(void);
+ cTimer *CurrentTimer(void);
+ void SetHelpKeys(void);
++#ifdef USE_TIMERINFO
++ void ActualiseDiskStatus(void);
++ bool actualiseDiskStatus;
++#endif /* TIMERINFO */
++#ifdef USE_TIMERCMD
++ eOSState Commands(eKeys Key = kNone);
++#endif /* TIMERCMD */
+ public:
+ cMenuTimers(void);
+ virtual ~cMenuTimers();
++#ifdef USE_TIMERINFO
++ virtual void Display(void);
++#endif /* TIMERINFO */
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuTimers"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuTimers::cMenuTimers(void)
++#ifdef USE_TIMERINFO
++:cOsdMenu(tr("Timers"), 3, CHNUMWIDTH, 10, 6, 6)
++#else
+ :cOsdMenu(tr("Timers"), 2, CHNUMWIDTH, 10, 6, 6)
++#endif /* TIMERINFO */
+ {
+ helpKeys = -1;
+ for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) {
+@@ -855,6 +1098,9 @@ cMenuTimers::cMenuTimers(void)
+ SetCurrent(First());
+ SetHelpKeys();
+ Timers.IncBeingEdited();
++#ifdef USE_TIMERINFO
++ actualiseDiskStatus = true;
++#endif /* TIMERINFO */
+ }
+
+ cMenuTimers::~cMenuTimers()
+@@ -893,7 +1139,11 @@ eOSState cMenuTimers::OnOff(void)
+ timer->OnOff();
+ timer->SetEventFromSchedule();
+ RefreshCurrent();
++#ifdef USE_TIMERINFO
++ Display();
++#else
+ DisplayCurrent(true);
++#endif /* TIMERINFO */
+ if (timer->FirstDay())
+ isyslog("timer %s first day set to %s", *timer->ToDescr(), *timer->PrintFirstDay());
+ else
+@@ -952,6 +1202,117 @@ eOSState cMenuTimers::Info(void)
+ return osContinue;
+ }
+
++#ifdef USE_TIMERCMD
++#define CHECK_2PTR_NULL(x_,y_) ((x_)? ((y_)? y_:""):"")
++
++eOSState cMenuTimers::Commands(eKeys Key)
++{
++ if (HasSubMenu() || Count() == 0)
++ return osContinue;
++ cTimer *ti = CurrentTimer();
++ if (ti) {
++ const cEvent *pEvent = ti->Event();
++ int iRecNumber=0;
++
++ if (!pEvent) {
++ Timers.SetEvents();
++ pEvent = ti->Event();
++ }
++ if (pEvent) {
++ // create a dummy recording to get the real filename
++ cRecording *rc_dummy = new cRecording(ti, pEvent);
++ Recordings.Load();
++ cRecording *rc = Recordings.GetByName(rc_dummy->FileName());
++
++ delete rc_dummy;
++ if (rc)
++ iRecNumber=rc->Index() + 1;
++ }
++ // TODO: Geht das so...?
++ // Parameter format TimerNumber 'ChannelId' Start Stop 'Titel' 'Subtitel' 'file' RecNumer
++ // 1 2 3 4 5 6 7 8
++ cString parameter = cString::sprintf("%d '%s' %d %d '%s' '%s' '%s' %d", ti->Index(),
++ *ti->Channel()->GetChannelID().ToString(),
++ (int)ti->StartTime(),
++ (int)ti->StopTime(),
++ CHECK_2PTR_NULL(pEvent, pEvent->Title()),
++ CHECK_2PTR_NULL(pEvent, pEvent->ShortText()),
++ ti->File(),
++ iRecNumber);
++ isyslog("timercmd: %s", *parameter);
++ cMenuCommands *menu;
++ eOSState state = AddSubMenu(menu = new cMenuCommands(tr("Timer commands"), &TimerCommands, parameter));
++ if (Key != kNone)
++ state = menu->ProcessKey(Key);
++ return state;
++ }
++ return osContinue;
++}
++#endif /* TIMERCMD */
++
++#ifdef USE_TIMERINFO
++void cMenuTimers::ActualiseDiskStatus(void)
++{
++ if (!actualiseDiskStatus || !Count())
++ return;
++
++ // compute free disk space
++ int freeMB, freeMinutes, runshortMinutes;
++ VideoDiskSpace(&freeMB);
++ freeMinutes = int(double(freeMB) * 1.1 / MB_PER_MINUTE); // overestimate by 10 percent
++ runshortMinutes = freeMinutes / 5; // 20 Percent
++
++ // fill entries list
++ cTimerEntry *entry;
++ cList<cTimerEntry> entries;
++ for (cOsdItem *item = First(); item; item = Next(item))
++ entries.Add(new cTimerEntry((cMenuTimerItem *)item));
++
++ // search last start time
++ time_t last = 0;
++ for (entry = entries.First(); entry; entry = entries.Next(entry))
++ last = max(entry->startTime(), last);
++
++ // add entries for repeating timers
++ for (entry = entries.First(); entry; entry = entries.Next(entry))
++ if (entry->repTimer() && !entry->isDummy())
++ for (time_t start = cTimer::IncDay(entry->startTime(), 1);
++ start <= last;
++ start = cTimer::IncDay(start, 1))
++ if (entry->Timer()->DayMatches(start))
++ entries.Add(new cTimerEntry(entry->Timer(), start));
++
++ // set the disk-status
++ entries.Sort();
++ for (entry = entries.First(); entry; entry = entries.Next(entry)) {
++ char status = ' ';
++ if (entry->active()) {
++ freeMinutes -= entry->duration();
++ status = freeMinutes > runshortMinutes ? '+' : freeMinutes > 0 ? '~' /* ± 177 +/- */ : '-';
++ }
++ entry->SetDiskStatus(status);
++#ifdef DEBUG_TIMER_INFO
++ dsyslog("timer-info: %c | %d | %s | %s | %3d | %+5d -> %+5d",
++ status,
++ entry->startTime(),
++ entry->active() ? "aktiv " : "n.akt.",
++ entry->repTimer() ? entry->isDummy() ? " dummy " : "mehrmalig" : "einmalig ",
++ entry->duration(),
++ entry->active() ? freeMinutes + entry->duration() : freeMinutes,
++ freeMinutes);
++#endif
++ }
++
++ actualiseDiskStatus = false;
++}
++
++void cMenuTimers::Display(void)
++{
++ ActualiseDiskStatus();
++ cOsdMenu::Display();
++}
++#endif /* TIMERINFO */
++
+ eOSState cMenuTimers::ProcessKey(eKeys Key)
+ {
+ int TimerNumber = HasSubMenu() ? Count() : -1;
+@@ -960,18 +1321,40 @@ eOSState cMenuTimers::ProcessKey(eKeys K
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk: return Edit();
++#ifdef USE_TIMERINFO
++ case kRed: actualiseDiskStatus = true;
++ state = OnOff(); break; // must go through SetHelpKeys()!
++#else
+ case kRed: state = OnOff(); break; // must go through SetHelpKeys()!
++#endif /* TIMERINFO */
+ case kGreen: return New();
++#ifdef USE_TIMERINFO
++ case kYellow: actualiseDiskStatus = true;
++ state = Delete(); break;
++#else
+ case kYellow: state = Delete(); break;
++#endif /* TIMERINFO */
+ case kInfo:
+ case kBlue: return Info();
+ break;
++#ifdef USE_TIMERCMD
++ case k1...k9: return Commands(Key);
++ case k0: return (TimerCommands.Count()? Commands():osContinue);
++#endif /* TIMERCMD */
+ default: break;
+ }
+ }
++#ifdef USE_TIMERINFO
++ if (TimerNumber >= 0 && !HasSubMenu()) {
++ if (Timers.Get(TimerNumber)) // a newly created timer was confirmed with Ok
++ Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true);
++ Sort();
++ actualiseDiskStatus = true;
++#else
+ if (TimerNumber >= 0 && !HasSubMenu() && Timers.Get(TimerNumber)) {
+ // a newly created timer was confirmed with Ok
+ Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true);
++#endif /* TIMERINFO */
+ Display();
+ }
+ if (Key != kNone)
+@@ -1001,6 +1384,9 @@ void cMenuEvent::Display(void)
+ {
+ cOsdMenu::Display();
+ DisplayMenu()->SetEvent(event);
++#ifdef USE_GRAPHTFT
++ cStatus::MsgOsdSetEvent(event);
++#endif /* GRAPHTFT */
+ if (event->Description())
+ cStatus::MsgOsdTextItem(event->Description());
+ }
+@@ -1048,7 +1434,12 @@ public:
+ const cChannel *channel;
+ bool withDate;
+ int timerMatch;
++#ifdef USE_LIEMIEXT
++ bool withBar;
++ cMenuScheduleItem(const cEvent *Event, cChannel *Channel = NULL, bool WithDate = false, bool WithBar = false);
++#else
+ cMenuScheduleItem(const cEvent *Event, cChannel *Channel = NULL, bool WithDate = false);
++#endif /* LIEMIEXT */
+ static void SetSortMode(eScheduleSortMode SortMode) { sortMode = SortMode; }
+ static void IncSortMode(void) { sortMode = eScheduleSortMode((sortMode == ssmAllAll) ? ssmAllThis : sortMode + 1); }
+ static eScheduleSortMode SortMode(void) { return sortMode; }
+@@ -1058,12 +1449,19 @@ public:
+
+ cMenuScheduleItem::eScheduleSortMode cMenuScheduleItem::sortMode = ssmAllThis;
+
++#ifdef USE_LIEMIEXT
++cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event, cChannel *Channel, bool WithDate, bool WithBar)
++#else
+ cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event, cChannel *Channel, bool WithDate)
++#endif /* LIEMIEXT */
+ {
+ event = Event;
+ channel = Channel;
+ withDate = WithDate;
+ timerMatch = tmNone;
++#ifdef USE_LIEMIEXT
++ withBar = WithBar;
++#endif /* LIEMIEXT */
+ Update(true);
+ }
+
+@@ -1078,7 +1476,29 @@ int cMenuScheduleItem::Compare(const cLi
+ return r;
+ }
+
++#ifdef USE_LIEMIEXT
++static const char * const ProgressBar[7] =
++{
++ "[ ]",
++ "[| ]",
++ "[|| ]",
++ "[||| ]",
++ "[|||| ]",
++ "[||||| ]",
++ "[||||||]"
++};
++#endif /* LIEMIEXT */
++
++#ifdef USE_WAREAGLEICON
++static const char *TimerMatchChars[9] =
++{
++ " ", "t", "T",
++ ICON_BLANK, ICON_CLOCK_UH, ICON_CLOCK,
++ ICON_BLANK_UTF8, ICON_CLOCK_UH_UTF8, ICON_CLOCK_UTF8
++};
++#else
+ static const char *TimerMatchChars = " tT";
++#endif /* WAREAGLEICON */
+
+ bool cMenuScheduleItem::Update(bool Force)
+ {
+@@ -1087,17 +1507,54 @@ bool cMenuScheduleItem::Update(bool Forc
+ Timers.GetMatch(event, &timerMatch);
+ if (Force || timerMatch != OldTimerMatch) {
+ cString buffer;
++#ifdef USE_WAREAGLEICON
++ const char *t = Setup.WarEagleIcons ? IsLangUtf8() ? TimerMatchChars[timerMatch+6] : TimerMatchChars[timerMatch+3] : TimerMatchChars[timerMatch];
++ const char *v = event->Vps() && (event->Vps() - event->StartTime()) ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_VPS_UTF8 : ICON_VPS : "V" : " ";
++ const char *r = event->SeenWithin(30) && event->IsRunning() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_RUNNING_UTF8 : ICON_RUNNING : "*" : " ";
++#else
+ char t = TimerMatchChars[timerMatch];
+ char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' ';
+ char r = event->SeenWithin(30) && event->IsRunning() ? '*' : ' ';
++#endif /* WAREAGLEICON */
+ const char *csn = channel ? channel->ShortName(true) : NULL;
+ cString eds = event->GetDateString();
+ if (channel && withDate)
++#ifdef USE_WAREAGLEICON
++ buffer = cString::sprintf("%d\t%.*s\t%.*s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#else
+ buffer = cString::sprintf("%d\t%.*s\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
+ else if (channel)
++#ifdef USE_LIEMIEXT
++ if (Setup.ShowProgressBar && withBar) {
++ int progress = (int)roundf( (float)(time(NULL) - event->StartTime()) / (float)(event->Duration()) * 6.0 );
++ if (progress < 0) progress = 0;
++ else if (progress > 6) progress = 6;
++#ifdef USE_WAREAGLEICON
++ buffer = cString::sprintf("%d\t%.*s\t%s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), ProgressBar[progress], t, v, r, event->Title());
++#else
++ buffer = cString::sprintf("%d\t%.*s\t%s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), ProgressBar[progress], t, v, r, event->Title());
++#endif /* WAREAGLEICON */
++ }
++ else
++#ifdef USE_WAREAGLEICON
++ buffer = cString::sprintf("%d\t%.*s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#else
++ buffer = cString::sprintf("%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
++#else
++#ifdef USE_WAREAGLEICON
++ buffer = cString::sprintf("%d\t%.*s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#else
+ buffer = cString::sprintf("%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
++#endif /* LIEMIEXT */
+ else
++#ifdef USE_WAREAGLEICON
++ buffer = cString::sprintf("%.*s\t%s\t%s%s%s\t%s", Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#else
+ buffer = cString::sprintf("%.*s\t%s\t%c%c%c\t%s", Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
+ SetText(buffer);
+ result = true;
+ }
+@@ -1123,13 +1580,21 @@ public:
+ static void SetCurrentChannel(int ChannelNr) { currentChannel = ChannelNr; }
+ static const cEvent *ScheduleEvent(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return now ? "MenuWhatsOnNow" : "MenuWhatsOnNext"; }
++ virtual void Display(void);
++#endif /* GRAPHTFT */
+ };
+
+ int cMenuWhatsOn::currentChannel = 0;
+ const cEvent *cMenuWhatsOn::scheduleEvent = NULL;
+
+ cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr)
++#ifdef USE_LIEMIEXT
++:cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, 7, 6, 4, 4)
++#else
+ :cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, 7, 6, 4)
++#endif /* LIEMIEXT */
+ {
+ now = Now;
+ helpKeys = -1;
+@@ -1141,7 +1606,11 @@ cMenuWhatsOn::cMenuWhatsOn(const cSchedu
+ if (Schedule) {
+ const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent();
+ if (Event)
++#ifdef USE_LIEMIEXT
++ Add(new cMenuScheduleItem(Event, Channel, false, Now), Channel->Number() == CurrentChannelNr);
++#else
+ Add(new cMenuScheduleItem(Event, Channel), Channel->Number() == CurrentChannelNr);
++#endif /* LIEMIEXT */
+ }
+ }
+ }
+@@ -1150,6 +1619,19 @@ cMenuWhatsOn::cMenuWhatsOn(const cSchedu
+ SetHelpKeys();
+ }
+
++#ifdef USE_GRAPHTFT
++void cMenuWhatsOn::Display(void)
++{
++ cOsdMenu::Display();
++
++ if (Count() > 0) {
++ int ni = 0;
++ for (cOsdItem *item = First(); item; item = Next(item))
++ cStatus::MsgOsdEventItem(((cMenuScheduleItem*)item)->event, item->Text(), ni++, Count());
++ }
++}
++#endif /* GRAPHTFT */
++
+ bool cMenuWhatsOn::Update(void)
+ {
+ bool result = false;
+@@ -1290,6 +1772,10 @@ public:
+ cMenuSchedule(void);
+ virtual ~cMenuSchedule();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSchedule"; }
++ virtual void Display(void);
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSchedule::cMenuSchedule(void)
+@@ -1315,6 +1801,19 @@ cMenuSchedule::~cMenuSchedule()
+ cMenuWhatsOn::ScheduleEvent(); // makes sure any posted data is cleared
+ }
+
++#ifdef USE_GRAPHTFT
++void cMenuSchedule::Display(void)
++{
++ cOsdMenu::Display();
++
++ if (Count() > 0) {
++ int ni = 0;
++ for (cOsdItem *item = First(); item; item = Next(item))
++ cStatus::MsgOsdEventItem(((cMenuScheduleItem*)item)->event, item->Text(), ni++, Count());
++ }
++}
++#endif /* GRAPHTFT */
++
+ void cMenuSchedule::PrepareScheduleAllThis(const cEvent *Event, const cChannel *Channel)
+ {
+ Clear();
+@@ -1548,6 +2047,7 @@ eOSState cMenuSchedule::ProcessKey(eKeys
+
+ // --- cMenuCommands ---------------------------------------------------------
+
++#ifndef USE_TIMERCMD
+ class cMenuCommands : public cOsdMenu {
+ private:
+ cCommands *commands;
+@@ -1557,7 +2057,11 @@ public:
+ cMenuCommands(const char *Title, cCommands *Commands, const char *Parameters = NULL);
+ virtual ~cMenuCommands();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuCommands"; }
++#endif /* GRAPHTFT */
+ };
++#endif /* TIMERCMD */
+
+ cMenuCommands::cMenuCommands(const char *Title, cCommands *Commands, const char *Parameters)
+ :cOsdMenu(Title)
+@@ -1579,6 +2083,12 @@ eOSState cMenuCommands::Execute(void)
+ cCommand *command = commands->Get(Current());
+ if (command) {
+ bool confirmed = true;
++#ifdef USE_CMDSUBMENU
++ if (command->hasChilds()) {
++ AddSubMenu(new cMenuCommands(command->Title(), command->getChilds(), parameters));
++ return osContinue;
++ }
++#endif /* CMDSUBMENU */
+ if (command->Confirm())
+ confirmed = Interface->Confirm(cString::sprintf("%s?", command->Title()));
+ if (confirmed) {
+@@ -1629,6 +2139,9 @@ public:
+ cMenuCam(cCamSlot *CamSlot);
+ virtual ~cMenuCam();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuCam"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuCam::cMenuCam(cCamSlot *CamSlot)
+@@ -1808,6 +2321,9 @@ public:
+ cMenuRecording(const cRecording *Recording, bool WithButtons = false);
+ virtual void Display(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuRecording"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuRecording::cMenuRecording(const cRecording *Recording, bool WithButtons)
+@@ -1823,6 +2339,9 @@ void cMenuRecording::Display(void)
+ {
+ cOsdMenu::Display();
+ DisplayMenu()->SetRecording(recording);
++#ifdef USE_GRAPHTFT
++ cStatus::MsgOsdSetRecording(recording);
++#endif /* GRAPHTFT */
+ if (recording->Info()->Description())
+ cStatus::MsgOsdTextItem(recording->Info()->Description());
+ }
+@@ -1883,7 +2402,11 @@ cMenuRecordingItem::cMenuRecordingItem(c
+ fileName = strdup(Recording->FileName());
+ name = NULL;
+ totalEntries = newEntries = 0;
++#ifdef USE_LIEMIEXT
++ SetText(Recording->Title('\t', true, Level, false));
++#else
+ SetText(Recording->Title('\t', true, Level));
++#endif /* LIEMIEXT */
+ if (*Text() == '\t')
+ name = strdup(Text() + 2); // 'Text() + 2' to skip the two '\t'
+ }
+@@ -1899,13 +2422,203 @@ void cMenuRecordingItem::IncrementCounte
+ totalEntries++;
+ if (New)
+ newEntries++;
++#ifdef USE_LIEMIEXT
++#ifdef USE_WAREAGLEICON
++ switch (Setup.ShowRecTime + Setup.ShowRecDate + Setup.ShowRecLength) {
++ case 0:
++ if (Setup.WarEagleIcons)
++ SetText(cString::sprintf("%s %s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER, name));
++ else
++ SetText(cString::sprintf("%s", name));
++ break;
++ case 1:
++ if (Setup.WarEagleIcons)
++ SetText(cString::sprintf("%s %d\t%s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER, totalEntries, name));
++ else
++ SetText(cString::sprintf("%d\t%s", totalEntries, name));
++ break;
++ case 2:
++ default:
++ if (Setup.WarEagleIcons)
++ SetText(cString::sprintf("%s (%d/%d)\t%s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER, totalEntries, newEntries, name));
++ else
++ SetText(cString::sprintf("%d\t%d\t%s", totalEntries, newEntries, name));
++ break;
++ case 3:
++ if (Setup.WarEagleIcons)
++ SetText(cString::sprintf("%s (%d/%d)\t\t%s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER, totalEntries, newEntries, name));
++ else
++ SetText(cString::sprintf("%d\t%d\t\t%s", totalEntries, newEntries, name));
++ break;
++ }
++#else
++ switch (Setup.ShowRecTime + Setup.ShowRecDate + Setup.ShowRecLength) {
++ case 0:
++ SetText(cString::sprintf("%s", name));
++ break;
++ case 1:
++ SetText(cString::sprintf("%d\t%s", totalEntries, name));
++ break;
++ case 2:
++ default:
++ SetText(cString::sprintf("%d\t%d\t%s", totalEntries, newEntries, name));
++ break;
++ case 3:
++ SetText(cString::sprintf("%d\t%d\t\t%s", totalEntries, newEntries, name));
++ break;
++ }
++#endif /* WAREAGLEICON */
++#else
++#ifdef USE_WAREAGLEICON
++ if (Setup.WarEagleIcons)
++ SetText(cString::sprintf("%s (%d/%d)\t%s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER, totalEntries, newEntries, name));
++ else
++#endif /* WAREAGLEICON */
+ SetText(cString::sprintf("%d\t%d\t%s", totalEntries, newEntries, name));
++#endif /* LIEMIEXT */
+ }
+
++#ifdef USE_LIEMIEXT
++// --- cMenuRenameRecording --------------------------------------------------
++
++class cMenuRenameRecording : public cOsdMenu {
++private:
++ int lifetime;
++ int priority;
++ char name[MaxFileName];
++ char path[MaxFileName];
++ cOsdItem *marksItem, *resumeItem;
++ bool isResume, isMarks;
++ cRecording *recording;
++public:
++ cMenuRenameRecording(cRecording *Recording);
++ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuRenameRecording"; }
++#endif /* GRAPHTFT */
++};
++
++cMenuRenameRecording::cMenuRenameRecording(cRecording *Recording)
++:cOsdMenu(tr("Rename recording"), 12)
++{
++ cMarks marks;
++
++ recording = Recording;
++ priority = recording->priority;
++ lifetime = recording->lifetime;
++
++ char* p = strrchr(recording->Name(), '~');
++ if (p) {
++ p++;
++ Utf8Strn0Cpy(name, p, sizeof(name));
++ Utf8Strn0Cpy(path, recording->Name(), sizeof(path));
++ p = strrchr(path, '~');
++ if (p)
++ p[0] = 0;
++ }
++ else {
++ Utf8Strn0Cpy(name, recording->Name(), sizeof(name));
++ Utf8Strn0Cpy(path, "", sizeof(path));
++ }
++ Add(new cMenuEditStrItem(tr("Name"), name, sizeof(name), tr(FileNameChars)));
++ Add(new cMenuEditRecPathItem(tr("Path"), path, sizeof(path) ));
++ Add(new cMenuEditIntItem(tr("Priority"), &priority, 0, MAXPRIORITY ));
++ Add(new cMenuEditIntItem(tr("Lifetime"), &lifetime, 0, MAXLIFETIME ));
++
++ Add(new cOsdItem("", osUnknown, false));
++
++ Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Date"), *DayDateTime(recording->start)), osUnknown, false));
++
++ cChannel *channel = Channels.GetByChannelID(((cRecordingInfo *)recording->Info())->ChannelID());
++ if (channel)
++ Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Channel"), *ChannelString(channel, 0)), osUnknown, false));
++
++ cIndexFile *index = new cIndexFile(recording->FileName(), false);
++ int recLen = 0;
++ if (index) {
++ recLen = index->Last() / FRAMESPERSEC;
++ Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Length"), *IndexToHMSF(index->Last())), osUnknown, false));
++ delete index;
++ }
++
++ int dirSize = DirSizeMB(recording->FileName());
++ cString bitRate = recLen ? cString::sprintf(" (%.2f MBit/s)", 8.0 * dirSize / recLen) : cString("");
++ Add(new cOsdItem((dirSize > 9999) ? cString::sprintf("%s:\t%.2f GB%s", tr("Size"), dirSize / 1024.0, *bitRate) : cString::sprintf("%s:\t%d MB%s", tr("Size"), dirSize, *bitRate), osUnknown, false));
++
++ Add(new cOsdItem("", osUnknown, false));
++
++ isMarks = marks.Load(recording->FileName()) && marks.Count();
++ marksItem = new cOsdItem(tr("Delete marks information?"), osUser1, isMarks);
++ Add(marksItem);
++
++ cResumeFile ResumeFile(recording->FileName());
++ isResume = (ResumeFile.Read() != -1);
++ resumeItem = new cOsdItem(tr("Delete resume information?"), osUser2, isResume);
++ Add(resumeItem);
++}
++
++eOSState cMenuRenameRecording::ProcessKey(eKeys Key)
++{
++ eOSState state = cOsdMenu::ProcessKey(Key);
++
++ if (state == osUnknown) {
++ if (Key == kOk) {
++ char buffer[MaxFileName];
++ if (Utf8StrLen(path))
++ snprintf(buffer, sizeof(buffer), "%s~%s", path, name);
++ else
++ snprintf(buffer, sizeof(buffer), "%s", name);
++ if (recording->Rename(buffer, &priority, &lifetime)) {
++ Recordings.ChangeState();
++ Recordings.TouchUpdate();
++ return osRecordings;
++ }
++ else
++ Skins.Message(mtError, tr("Error while accessing recording!"));
++ }
++ return osContinue;
++ }
++ else if (state == osUser1) {
++ if (isMarks && Interface->Confirm(tr("Delete marks information?"))) {
++ cMarks marks;
++ marks.Load(recording->FileName());
++ cMark *mark = marks.First();
++ while (mark) {
++ cMark *nextmark = marks.Next(mark);
++ marks.Del(mark);
++ mark = nextmark;
++ }
++ marks.Save();
++ isMarks = false;
++ marksItem->SetSelectable(isMarks);
++ SetCurrent(First());
++ Display();
++ }
++ return osContinue;
++ }
++ else if (state == osUser2) {
++ if (isResume && Interface->Confirm(tr("Delete resume information?"))) {
++ cResumeFile ResumeFile(recording->FileName());
++ ResumeFile.Delete();
++ isResume = false;
++ resumeItem->SetSelectable(isResume);
++ SetCurrent(First());
++ Display();
++ }
++ return osContinue;
++ }
++ return state;
++}
++#endif /* LIEMIEXT */
++
+ // --- cMenuRecordings -------------------------------------------------------
+
+ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus)
++#ifdef USE_LIEMIEXT
++:cOsdMenu(Base ? Base : tr("Recordings"), 9, 7, 7)
++#else
+ :cOsdMenu(Base ? Base : tr("Recordings"), 9, 7)
++#endif /* LIEMIEXT */
+ {
+ base = Base ? strdup(Base) : NULL;
+ level = Setup.RecordingDirs ? Level : -1;
+@@ -1982,7 +2695,12 @@ void cMenuRecordings::Set(bool Refresh)
+ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
+ if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == '~')) {
+ cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
++#ifdef USE_PINPLUGIN
++ if ((*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0))
++ && (!cStatus::MsgReplayProtected(GetRecording(Item), Item->Name(), base, Item->IsDirectory(), true))) {
++#else
+ if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0)) {
++#endif /* PINPLUGIN */
+ Add(Item);
+ LastItem = Item;
+ free(LastItemText);
+@@ -2032,13 +2750,43 @@ eOSState cMenuRecordings::Play(void)
+ {
+ cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
+ if (ri) {
++#ifdef USE_PINPLUGIN
++ if (cStatus::MsgReplayProtected(GetRecording(ri), ri->Name(), base, ri->IsDirectory()) == true)
++ return osContinue;
++#endif /* PINPLUGIN */
+ if (ri->IsDirectory())
+ Open();
+ else {
+ cRecording *recording = GetRecording(ri);
+ if (recording) {
++#ifdef USE_DVDARCHIVE
++ int mountRet = MOUNT_DVD_REPLAY;
++ if (recording->IsOnlyOnDvd()) {
++ mountRet = recording->MountDvd();
++ }
++ if (mountRet == MOUNT_DVD_REPLAY) {
++ cReplayControl::SetRecording(recording->FileName(), recording->Title());
++ return osReplay;
++ }
++ else if (mountRet == MOUNT_DVD_LAUNCH_DVD_PLUGIN) {
++ //launch DVD plugin here
++ cPlugin *p = cPluginManager::GetPlugin("dvd");
++ cOsdObject *osd = NULL;
++ if (p) {
++ osd = p->MainMenuAction();
++ delete osd;
++ osd = NULL;
++ return osEnd;
++ }
++ else {
++ Skins.Message(mtError, tr("DVD plugin is not installed!"));
++ Skins.Flush();
++ }
++ }
++#else
+ cReplayControl::SetRecording(recording->FileName(), recording->Title());
+ return osReplay;
++#endif /* DVDARCHIVE */
+ }
+ }
+ }
+@@ -2136,12 +2884,34 @@ eOSState cMenuRecordings::Commands(eKeys
+ return osContinue;
+ }
+
++#ifdef USE_LIEMIEXT
++eOSState cMenuRecordings::Rename(void)
++{
++ if (HasSubMenu() || Count() == 0)
++ return osContinue;
++ cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
++ if (ri && !ri->IsDirectory()) {
++ cRecording *recording = GetRecording(ri);
++ if (recording)
++ return AddSubMenu(new cMenuRenameRecording(recording));
++ }
++ return osContinue;
++}
++#endif /* LIEMIEXT */
++
+ eOSState cMenuRecordings::ProcessKey(eKeys Key)
+ {
+ bool HadSubMenu = HasSubMenu();
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if (state == osUnknown) {
++#ifdef USE_SORTRECORDS
++ const char *RecordingsSortModeTexts[MAXSORTMODES];
++ RecordingsSortModeTexts[0] = tr("main dir alphabetically, subdirs flexible");
++ RecordingsSortModeTexts[1] = tr("main dir by date, subdirs flexible");
++ RecordingsSortModeTexts[2] = tr("all alphabetically");
++ RecordingsSortModeTexts[3] = tr("all by date");
++#endif /* SORTRECORDS */
+ switch (Key) {
+ case kPlay:
+ case kOk: return Play();
+@@ -2150,7 +2920,26 @@ eOSState cMenuRecordings::ProcessKey(eKe
+ case kYellow: return Delete();
+ case kInfo:
+ case kBlue: return Info();
++#ifdef USE_SORTRECORDS
++ case k0: Setup.RecordingsSortMode = ++Setup.RecordingsSortMode % MAXSORTMODES;
++ Set(true);
++ Skins.Message(mtStatus, cString::sprintf("%s %d: %s", tr("Sorting"), Setup.RecordingsSortMode, RecordingsSortModeTexts[Setup.RecordingsSortMode]));
++ return osContinue;
++ case k1...k7: return Commands(Key);
++ case k8: return Rename();
++ case k9: Recordings.ToggleSortOrder();
++ Set(true);
++ return osContinue;
++#elif defined (USE_LIEMIEXT)
++ case k0: DirOrderState = !DirOrderState;
++ Set(true);
++ return osContinue;
++ case k8: return Rename();
++ case k9:
++ case k1...k7: return Commands(Key);
++#else
+ case k1...k9: return Commands(Key);
++#endif /* LIEMIEXT & SORTRECORDS */
+ case kNone: if (Recordings.StateChanged(recordingsState))
+ Set(true);
+ break;
+@@ -2199,6 +2988,9 @@ void cMenuSetupBase::Store(void)
+ class cMenuSetupOSD : public cMenuSetupBase {
+ private:
+ const char *useSmallFontTexts[3];
++#ifdef USE_LIEMIEXT
++ const char *mainMenuTitle[MAXMAINMENUTITLE];
++#endif /* LIEMIEXT */
+ int osdLanguageIndex;
+ int numSkins;
+ int originalSkinIndex;
+@@ -2214,6 +3006,9 @@ public:
+ cMenuSetupOSD(void);
+ virtual ~cMenuSetupOSD();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupOsd"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupOSD::cMenuSetupOSD(void)
+@@ -2249,12 +3044,21 @@ void cMenuSetupOSD::Set(void)
+ useSmallFontTexts[0] = tr("never");
+ useSmallFontTexts[1] = tr("skin dependent");
+ useSmallFontTexts[2] = tr("always");
++#ifdef USE_LIEMIEXT
++ mainMenuTitle[0]=tr("default");
++ mainMenuTitle[1]=tr("VDR - text");
++ mainMenuTitle[2]=tr("text");
++ mainMenuTitle[3]=tr("VDR - version");
++#endif /* LIEMIEXT */
+ Clear();
+ SetSection(tr("OSD"));
+ Add(new cMenuEditStraItem(tr("Setup.OSD$Language"), &osdLanguageIndex, I18nNumLanguagesWithLocale(), &I18nLanguages()->At(0)));
+ Add(new cMenuEditStraItem(tr("Setup.OSD$Skin"), &skinIndex, numSkins, skinDescriptions));
+ if (themes.NumThemes())
+ Add(new cMenuEditStraItem(tr("Setup.OSD$Theme"), &themeIndex, themes.NumThemes(), themes.Descriptions()));
++#ifdef USE_WAREAGLEICON
++ Add(new cMenuEditBoolItem(tr("Setup.OSD$WarEagle icons"), &data.WarEagleIcons));
++#endif /* WAREAGLEICON */
+ Add(new cMenuEditIntItem( tr("Setup.OSD$Left"), &data.OSDLeft, 0, MAXOSDWIDTH));
+ Add(new cMenuEditIntItem( tr("Setup.OSD$Top"), &data.OSDTop, 0, MAXOSDHEIGHT));
+ Add(new cMenuEditIntItem( tr("Setup.OSD$Width"), &data.OSDWidth, MINOSDWIDTH, MAXOSDWIDTH));
+@@ -2276,6 +3080,24 @@ void cMenuSetupOSD::Set(void)
+ Add(new cMenuEditBoolItem(tr("Setup.OSD$Scroll wraps"), &data.MenuScrollWrap));
+ Add(new cMenuEditBoolItem(tr("Setup.OSD$Menu key closes"), &data.MenuKeyCloses));
+ Add(new cMenuEditBoolItem(tr("Setup.OSD$Recording directories"), &data.RecordingDirs));
++#ifdef USE_LIEMIEXT
++ Add(new cMenuEditStraItem(tr("Setup.OSD$Main menu title"), &data.MainMenuTitle, MAXMAINMENUTITLE, mainMenuTitle));
++ if (data.MainMenuTitle == 1 || data.MainMenuTitle == 2)
++ Add(new cMenuEditStrItem(tr("Setup.OSD$- Text"), data.CustomMainMenuTitle, sizeof(data.CustomMainMenuTitle)));
++ Add(new cMenuEditBoolItem(tr("Setup.OSD$Main menu command position"), &data.MenuCmdPosition, tr("bottom"), tr("top")));
++#endif /* LIEMIEXT */
++#ifdef USE_VALIDINPUT
++ Add(new cMenuEditBoolItem(tr("Setup.OSD$Show valid input"), &data.ShowValidInput));
++#endif /* VALIDINPUT */
++#ifdef USE_SOFTOSD
++ Add(new cMenuEditBoolItem(tr("Setup.OSD$Use SoftOSD"), &data.UseSoftOsd));
++ if (data.UseSoftOsd) {
++ Add(new cMenuEditIntItem( tr("Setup.OSD$SoftOSD Rate"), &data.SoftOsdRate, 10, 100));
++ Add(new cMenuEditIntItem( tr("Setup.OSD$SoftOSD FadeIn Steps"), &data.SoftOsdFadeinSteps, 1, 100));
++ Add(new cMenuEditIntItem( tr("Setup.OSD$SoftOSD FadeOut Steps"), &data.SoftOsdFadeoutSteps, 1, 100));
++ Add(new cMenuEditBoolItem(tr("Setup.OSD$SoftOSD Palette Only"), &data.SoftOsdPaletteOnly));
++ }
++#endif /* SOFTOSD */
+ SetCurrent(Get(current));
+ Display();
+ }
+@@ -2324,6 +3146,12 @@ eOSState cMenuSetupOSD::ProcessKey(eKeys
+
+ int oldSkinIndex = skinIndex;
+ int oldOsdLanguageIndex = osdLanguageIndex;
++#ifdef USE_LIEMIEXT
++ int oldMainMenuTitle = data.MainMenuTitle;
++#endif /* LIEMIEXT */
++#ifdef USE_SOFTOSD
++ bool newUseSoftOsd = data.UseSoftOsd;
++#endif /* SOFTOSD */
+ eOSState state = cMenuSetupBase::ProcessKey(Key);
+
+ if (ModifiedApperance)
+@@ -2346,6 +3174,25 @@ eOSState cMenuSetupOSD::ProcessKey(eKeys
+ Set();
+ I18nSetLanguage(OriginalOSDLanguage);
+ }
++
++#ifdef USE_LIEMIEXT
++ if (data.MainMenuTitle != oldMainMenuTitle)
++ Set();
++#endif /* LIEMIEXT */
++#ifdef USE_SOFTOSD
++ if (data.UseSoftOsd != newUseSoftOsd)
++ Set();
++#endif /* SOFTOSD */
++#ifdef USE_CMDRECCMDI18N
++ if (Key == kOk) {
++ // try to load translated command files if available, otherwise fallback to defaults
++ LoadCommandsI18n(Commands, AddDirectory(ConfigDirectory, "commands.conf"), true);
++ LoadCommandsI18n(RecordingCommands, AddDirectory(ConfigDirectory, "reccmds.conf"), true);
++#ifdef USE_TIMERCMD
++ LoadCommandsI18n(TimerCommands, AddDirectory(ConfigDirectory, "timercmds.conf"), true);
++#endif /* TIMERCMD */
++ }
++#endif /* CMDRECCMDI18N */
+ return state;
+ }
+
+@@ -2353,12 +3200,18 @@ eOSState cMenuSetupOSD::ProcessKey(eKeys
+
+ class cMenuSetupEPG : public cMenuSetupBase {
+ private:
++#ifdef USE_NOEPG
++ const char *noEPGModes[2];
++#endif /* NOEPG */
+ int originalNumLanguages;
+ int numLanguages;
+ void Setup(void);
+ public:
+ cMenuSetupEPG(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupEpg"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupEPG::cMenuSetupEPG(void)
+@@ -2375,11 +3228,19 @@ void cMenuSetupEPG::Setup(void)
+ {
+ int current = Current();
+
++#ifdef USE_NOEPG
++ noEPGModes[0] = tr("blacklist");
++ noEPGModes[1] = tr("whitelist");
++#endif /* NOEPG */
++
+ Clear();
+
+ Add(new cMenuEditIntItem( tr("Setup.EPG$EPG scan timeout (h)"), &data.EPGScanTimeout));
+ Add(new cMenuEditIntItem( tr("Setup.EPG$EPG bugfix level"), &data.EPGBugfixLevel, 0, MAXEPGBUGFIXLEVEL));
+ Add(new cMenuEditIntItem( tr("Setup.EPG$EPG linger time (min)"), &data.EPGLinger, 0));
++#ifdef USE_LIEMIEXT
++ Add(new cMenuEditBoolItem(tr("Setup.EPG$Show progress bar"), &data.ShowProgressBar));
++#endif /* LIEMIEXT */
+ Add(new cMenuEditBoolItem(tr("Setup.EPG$Set system time"), &data.SetSystemTime));
+ if (data.SetSystemTime)
+ Add(new cMenuEditTranItem(tr("Setup.EPG$Use time from transponder"), &data.TimeTransponder, &data.TimeSource));
+@@ -2388,6 +3249,15 @@ void cMenuSetupEPG::Setup(void)
+ for (int i = 0; i < numLanguages; i++)
+ // TRANSLATORS: note the singular!
+ Add(new cMenuEditStraItem(tr("Setup.EPG$Preferred language"), &data.EPGLanguages[i], I18nLanguages()->Size(), &I18nLanguages()->At(0)));
++#ifdef USE_DDEPGENTRY
++ Add(new cMenuEditIntItem( tr("Setup.EPG$Period for double EPG search(min)"), &data.DoubleEpgTimeDelta));
++ Add(new cMenuEditBoolItem(tr("Setup.EPG$Extern double Epg entry"), &data.DoubleEpgAction, tr("adjust"), tr("delete")));
++ Add(new cMenuEditBoolItem(tr("Setup.EPG$Mix intern and extern EPG"), &data.MixEpgAction));
++ Add(new cMenuEditBoolItem(tr("Setup.EPG$Disable running VPS event"), &data.DisableVPS));
++#endif /* DDEPGENTRY */
++#ifdef USE_NOEPG
++ Add(new cMenuEditStraItem(tr("Setup.EPG$Mode of noEPG-Patch"), &data.noEPGMode, 2, noEPGModes));
++#endif /* NOEPG */
+
+ SetCurrent(Get(current));
+ Display();
+@@ -2444,6 +3314,10 @@ eOSState cMenuSetupEPG::ProcessKey(eKeys
+
+ class cMenuSetupDVB : public cMenuSetupBase {
+ private:
++#ifdef USE_DVBSETUP
++ const char *ChannelBlockers[7];
++ const char *ChannelBlockerModes[4];
++#endif /* DVBSETUP */
+ int originalNumAudioLanguages;
+ int numAudioLanguages;
+ int originalNumSubtitleLanguages;
+@@ -2454,6 +3328,9 @@ private:
+ public:
+ cMenuSetupDVB(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupDvb"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupDVB::cMenuSetupDVB(void)
+@@ -2482,6 +3359,21 @@ void cMenuSetupDVB::Setup(void)
+ {
+ int current = Current();
+
++#ifdef USE_DVBSETUP
++ ChannelBlockers[0] = tr("none");
++ ChannelBlockers[1] = tr("qam256");
++ ChannelBlockers[2] = tr("dvb-c");
++ ChannelBlockers[3] = tr("dvb-s");
++ ChannelBlockers[4] = tr("blacklist");
++ ChannelBlockers[5] = tr("whitelist");
++ ChannelBlockers[6] = tr("all");
++
++ ChannelBlockerModes[0] = tr("none");
++ ChannelBlockerModes[1] = tr("has decoder");
++ ChannelBlockerModes[2] = tr("is primary");
++ ChannelBlockerModes[3] = tr("has decoder + is primary");
++#endif /* DVBSETUP */
++
+ Clear();
+
+ Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
+@@ -2502,6 +3394,14 @@ void cMenuSetupDVB::Setup(void)
+ Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle foreground transparency"), &data.SubtitleFgTransparency, 0, 9));
+ Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle background transparency"), &data.SubtitleBgTransparency, 0, 10));
+ }
++#ifdef USE_DVBSETUP
++ Add(new cMenuEditBoolItem(tr("Setup.DVB$Use AC3-Transfer Fix"), &data.DolbyTransferFix));
++ Add(new cMenuEditStraItem(tr("Setup.DVB$Channel Blocker"), &data.ChannelBlocker, 7, ChannelBlockers));
++ Add(new cMenuEditStraItem(tr("Setup.DVB$Channel Blocker Filter Mode"), &data.ChannelBlockerMode, 4, ChannelBlockerModes));
++#endif /* DVBSETUP */
++#ifdef USE_SYNCEARLY
++ Add(new cMenuEditBoolItem(tr("Setup.DVB$Use Sync Early Patch"), &data.UseSyncEarlyPatch));
++#endif /* SYNCEARLY */
+
+ SetCurrent(Get(current));
+ Display();
+@@ -2583,6 +3483,9 @@ private:
+ public:
+ cMenuSetupLNB(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupLnb"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupLNB::cMenuSetupLNB(void)
+@@ -2597,6 +3500,23 @@ void cMenuSetupLNB::Setup(void)
+
+ Clear();
+
++#ifdef USE_LNBSHARE
++ int numSatDevices = 0;
++ for (int i = 0; i < cDevice::NumDevices(); i++) {
++ if (cDevice::GetDevice(i)->ProvidesSource(cSource::stSat)) numSatDevices++;
++ }
++ if (numSatDevices > 1) {
++ char tmp[30];
++ for (int i = 1; i <= cDevice::NumDevices(); i++) {
++ if (cDevice::GetDevice(i - 1)->ProvidesSource(cSource::stSat)) {
++ sprintf( tmp, tr("Setup.LNB$DVB device %d uses LNB No."), i);
++ Add(new cMenuEditIntItem( tmp, &data.CardUsesLNBnr[i - 1], 1, numSatDevices ));
++ }
++ }
++ }
++ Add(new cMenuEditBoolItem(tr("Setup.LNB$Log LNB usage"), &data.VerboseLNBlog));
++#endif /* LNBSHARE */
++
+ Add(new cMenuEditBoolItem(tr("Setup.LNB$Use DiSEqC"), &data.DiSEqC));
+ if (!data.DiSEqC) {
+ Add(new cMenuEditIntItem( tr("Setup.LNB$SLOF (MHz)"), &data.LnbSLOF));
+@@ -2613,6 +3533,10 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys
+ int oldDiSEqC = data.DiSEqC;
+ eOSState state = cMenuSetupBase::ProcessKey(Key);
+
++#ifdef USE_LNBSHARE
++ if (Key == kOk) cDevice::SetLNBnr();
++#endif /* LNBSHARE */
++
+ if (Key != kNone && data.DiSEqC != oldDiSEqC)
+ Setup();
+ return state;
+@@ -2663,6 +3587,9 @@ private:
+ public:
+ cMenuSetupCAM(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupCam"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupCAM::cMenuSetupCAM(void)
+@@ -2736,12 +3663,73 @@ eOSState cMenuSetupCAM::ProcessKey(eKeys
+ // --- cMenuSetupRecord ------------------------------------------------------
+
+ class cMenuSetupRecord : public cMenuSetupBase {
++#ifdef USE_SORTRECORDS
++private:
++ const char *RecordingsSortModeTexts[MAXSORTMODES];
++#endif /* SORTRECORDS */
++#ifdef USE_DELTIMESHIFTREC
++private:
++ const char *DelTimeshiftRecValues[3];
++#endif /* DELTIMESHIFTREC */
+ public:
+ cMenuSetupRecord(void);
++#ifdef USE_DVLVIDPREFER
++ eOSState ProcessKey(eKeys key);
++
++private:
++ void Set(void);
++
++ int tmpNVidPrefer,
++ tmpUseVidPrefer;
++#endif /* DVLVIDPREFER */
+ };
+
++#ifdef USE_DVLVIDPREFER
+ cMenuSetupRecord::cMenuSetupRecord(void)
+ {
++ Set();
++}
++
++eOSState cMenuSetupRecord::ProcessKey(eKeys key)
++{
++ eOSState s = cMenuSetupBase::ProcessKey(key);;
++
++ if (key != kNone) {
++ if (tmpNVidPrefer != data.nVidPrefer || tmpUseVidPrefer != data.UseVidPrefer) {
++ int cur = Current();
++
++ tmpNVidPrefer = data.nVidPrefer;
++ tmpUseVidPrefer = data.UseVidPrefer;
++
++ Clear();
++ Set();
++ SetCurrent(Get(cur));
++ Display();
++ cMenuSetupBase::ProcessKey(kNone);
++ return osContinue;
++ }
++ }
++ return s;
++}
++
++#else
++cMenuSetupRecord::cMenuSetupRecord(void)
++#endif /* DVLVIDPREFER */
++#ifdef USE_DVLVIDPREFER
++void cMenuSetupRecord::Set(void)
++#endif /* DVLVIDPREFER */
++{
++#ifdef USE_SORTRECORDS
++ RecordingsSortModeTexts[0] = tr("main dir alphabetically, subdirs flexible");
++ RecordingsSortModeTexts[1] = tr("main dir by date, subdirs flexible");
++ RecordingsSortModeTexts[2] = tr("all alphabetically");
++ RecordingsSortModeTexts[3] = tr("all by date");
++#endif /* SORTRECORDS */
++#ifdef USE_DELTIMESHIFTREC
++ DelTimeshiftRecValues[0] = tr("request");
++ DelTimeshiftRecValues[1] = tr("no");
++ DelTimeshiftRecValues[2] = tr("yes");
++#endif /* DELTIMESHIFTREC */
+ SetSection(tr("Recording"));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$Margin at start (min)"), &data.MarginStart));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$Margin at stop (min)"), &data.MarginStop));
+@@ -2750,14 +3738,61 @@ cMenuSetupRecord::cMenuSetupRecord(void)
+ Add(new cMenuEditIntItem( tr("Setup.Recording$Default lifetime (d)"), &data.DefaultLifetime, 0, MAXLIFETIME));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$Pause priority"), &data.PausePriority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$Pause lifetime (d)"), &data.PauseLifetime, 0, MAXLIFETIME));
++#ifdef USE_DOLBYINREC
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Record Dolby Digital"), &data.UseDolbyInRecordings));
++#endif /* DOLBYINREC */
++#ifdef USE_DVLVIDPREFER
++ tmpNVidPrefer = data.nVidPrefer;
++ tmpUseVidPrefer = data.UseVidPrefer;
++
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Video directory policy"), &data.UseVidPrefer));
++ if (data.UseVidPrefer != 0) {
++ char tmp[ 64 ];
++ Add(new cMenuEditIntItem(tr("Setup.Recording$Number of video directories"), &data.nVidPrefer, 1, DVLVIDPREFER_MAX));
++ for (int zz = 0; zz < data.nVidPrefer; zz++) {
++ sprintf(tmp, tr("Setup.Recording$Video %d priority"), zz);
++ Add(new cMenuEditIntItem(tmp, &data.VidPreferPrio[ zz ], 0, 99));
++ sprintf(tmp, tr("Setup.Recording$Video %d min. free MB"), zz);
++ Add(new cMenuEditIntItem(tmp, &data.VidPreferSize[ zz ], -1, 99999));
++ }
++ }
++#endif /* DVLVIDPREFER */
+ Add(new cMenuEditBoolItem(tr("Setup.Recording$Use episode name"), &data.UseSubtitle));
++#ifdef USE_DVLFRIENDLYFNAMES
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Friendly filenames"), &data.UseFriendlyFNames));
++#endif /* DVLFRIENDLYFNAMES */
+ Add(new cMenuEditBoolItem(tr("Setup.Recording$Use VPS"), &data.UseVps));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$VPS margin (s)"), &data.VpsMargin, 0));
+ Add(new cMenuEditBoolItem(tr("Setup.Recording$Mark instant recording"), &data.MarkInstantRecord));
+ Add(new cMenuEditStrItem( tr("Setup.Recording$Name instant recording"), data.NameInstantRecord, sizeof(data.NameInstantRecord)));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$Instant rec. time (min)"), &data.InstantRecordTime, 1, MAXINSTANTRECTIME));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$Max. video file size (MB)"), &data.MaxVideoFileSize, MINVIDEOFILESIZE, MAXVIDEOFILESIZE));
++#ifdef USE_HARDLINKCUTTER
++ Add(new cMenuEditIntItem( tr("Setup.Recording$Max. recording size (GB)"), &data.MaxRecordingSize, MINRECORDINGSIZE, MAXRECORDINGSIZE));
++#endif /* HARDLINKCUTTER */
+ Add(new cMenuEditBoolItem(tr("Setup.Recording$Split edited files"), &data.SplitEditedFiles));
++#ifdef USE_HARDLINKCUTTER
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Hard Link Cutter"), &data.HardLinkCutter));
++#endif /* HARDLINKCUTTER */
++#ifdef USE_DELTIMESHIFTREC
++ Add(new cMenuEditStraItem(tr("Setup.Recording$Delete timeshift recording"), &data.DelTimeshiftRec, 3, DelTimeshiftRecValues));
++#endif /* DELTIMESHIFTREC */
++#ifdef USE_LIEMIEXT
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Show date"), &data.ShowRecDate));
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Show time"), &data.ShowRecTime));
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Show length"), &data.ShowRecLength));
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Show end of timer"), &data.ShowTimerStop));
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++ Add(new cMenuEditStraItem(tr("Setup.Recording$Sort recordings by"), &data.RecordingsSortMode, MAXSORTMODES, RecordingsSortModeTexts));
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Sort directories before recordings"), &data.RecordingsSortDirsFirst));
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Cutter auto delete"), &data.CutterAutoDelete));
++#endif /* CUTTERQUEUE */
++#ifdef USE_CUTTIME
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Cutter adjust starttime"), &data.CutTime));
++#endif /* CUTTIME */
+ }
+
+ // --- cMenuSetupReplay ------------------------------------------------------
+@@ -2775,6 +3810,31 @@ cMenuSetupReplay::cMenuSetupReplay(void)
+ Add(new cMenuEditBoolItem(tr("Setup.Replay$Multi speed mode"), &data.MultiSpeedMode));
+ Add(new cMenuEditBoolItem(tr("Setup.Replay$Show replay mode"), &data.ShowReplayMode));
+ Add(new cMenuEditIntItem(tr("Setup.Replay$Resume ID"), &data.ResumeID, 0, 99));
++#ifdef USE_JUMPPLAY
++ Add(new cMenuEditBoolItem(tr("Setup.Replay$Jump&Play"), &data.JumpPlay));
++ Add(new cMenuEditBoolItem(tr("Setup.Replay$Play&Jump"), &data.PlayJump));
++ Add(new cMenuEditBoolItem(tr("Setup.Replay$Pause at last mark"), &data.PauseLastMark));
++ Add(new cMenuEditBoolItem(tr("Setup.Replay$Reload marks"), &data.ReloadMarks));
++#endif /* JUMPPLAY */
++#ifdef USE_LIEMIEXT
++ Add(new cMenuEditIntItem(tr("Setup.Replay$Skip Seconds"), &data.JumpSeconds));
++ Add(new cMenuEditIntItem(tr("Setup.Replay$Skip Seconds Slow"), &data.JumpSecondsSlow));
++#endif /* LIEMIEXT */
++#ifdef USE_DVDARCHIVE
++ static const char *dvddisplaymode[3];
++ dvddisplaymode[0]=tr("Setup.Replay$Length");
++ dvddisplaymode[1]=tr("Setup.Replay$Length / Number");
++ dvddisplaymode[2]=tr("Setup.Replay$Number");
++ Add(new cMenuEditStraItem(tr("Setup.Replay$DVD display mode"), &data.DvdDisplayMode,3,dvddisplaymode));
++ Add(new cMenuEditBoolItem(tr("Setup.Replay$DVD display leading zeros"), &data.DvdDisplayZeros));
++ static const char *dvdtraymode[4];
++ dvdtraymode[0]=tr("Setup.Replay$never");
++ dvdtraymode[1]=tr("Setup.Replay$on begin");
++ dvdtraymode[2]=tr("Setup.Replay$on end");
++ dvdtraymode[3]=tr("Setup.Replay$on begin and end");
++ Add(new cMenuEditStraItem(tr("Setup.Replay$Tray open"), &data.DvdTrayMode,4,dvdtraymode));
++ Add(new cMenuEditIntItem( tr("Setup.Replay$Limit DVD to speed"), &data.DvdSpeedLimit, 0, 50));
++#endif /* DVDARCHIVE */
+ }
+
+ void cMenuSetupReplay::Store(void)
+@@ -2787,13 +3847,48 @@ void cMenuSetupReplay::Store(void)
+ // --- cMenuSetupMisc --------------------------------------------------------
+
+ class cMenuSetupMisc : public cMenuSetupBase {
++#ifdef USE_VOLCTRL
++private:
++ const char *lrChannelGroupsTexts[3];
++ const char *lrForwardRewindTexts[3];
++ void Setup(void);
++#endif /* VOLCTRL */
+ public:
+ cMenuSetupMisc(void);
++#ifdef USE_VOLCTRL
++ virtual eOSState ProcessKey(eKeys Key);
++#endif /* VOLCTRL */
+ };
+
+ cMenuSetupMisc::cMenuSetupMisc(void)
+ {
++#ifdef USE_VOLCTRL
++ lrChannelGroupsTexts[0] = tr("no");
++ lrChannelGroupsTexts[1] = tr("Setup.Miscellaneous$only in channelinfo");
++ lrChannelGroupsTexts[2] = tr("yes");
++ lrForwardRewindTexts[0] = tr("no");
++ lrForwardRewindTexts[1] = tr("Setup.Miscellaneous$only in progress display");
++ lrForwardRewindTexts[2] = tr("yes");
++#endif /* VOLCTRL */
+ SetSection(tr("Miscellaneous"));
++#ifdef USE_VOLCTRL
++ Setup();
++}
++
++eOSState cMenuSetupMisc::ProcessKey(eKeys Key)
++{
++ int newLRVolumeControl = data.LRVolumeControl;
++ eOSState state = cMenuSetupBase::ProcessKey(Key);
++ if (Key != kNone && data.LRVolumeControl != newLRVolumeControl)
++ Setup();
++ return state;
++}
++
++void cMenuSetupMisc::Setup(void)
++{
++ int current = Current();
++ Clear();
++#endif /* VOLCTRL */
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. event timeout (min)"), &data.MinEventTimeout));
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. user inactivity (min)"), &data.MinUserInactivity));
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$SVDRP timeout (s)"), &data.SVDRPTimeout));
+@@ -2801,9 +3896,77 @@ cMenuSetupMisc::cMenuSetupMisc(void)
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Channel entry timeout (ms)"), &data.ChannelEntryTimeout, 0));
+ Add(new cMenuEditChanItem(tr("Setup.Miscellaneous$Initial channel"), &data.InitialChannel, tr("Setup.Miscellaneous$as before")));
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Initial volume"), &data.InitialVolume, -1, 255, tr("Setup.Miscellaneous$as before")));
++#ifdef USE_VOLCTRL
++ Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Volume ctrl with left/right"), &data.LRVolumeControl));
++ if (data.LRVolumeControl) {
++ Add(new cMenuEditStraItem(tr("Setup.Miscellaneous$Channelgroups with left/right"), &data.LRChannelGroups, 3, lrChannelGroupsTexts));
++ Add(new cMenuEditStraItem(tr("Setup.Miscellaneous$Search fwd/back with left/right"), &data.LRForwardRewind, 3, lrForwardRewindTexts));
++ }
++ SetCurrent(Get(current));
++ Display();
++#endif /* VOLCTRL */
+ Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Emergency exit"), &data.EmergencyExit));
++#ifdef USE_LIRCSETTINGS
++ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc repeat delay"), &data.LircRepeatDelay, 0, 1000));
++ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc repeat freq"), &data.LircRepeatFreq, 0, 1000));
++ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc repeat timeout"), &data.LircRepeatTimeout, 0, 5000));
++#endif /* LIRCSETTINGS */
++}
++
++#ifdef USE_LIVEBUFFER
++// --- cMenuSetupLiveBuffer --------------------------------------------------
++
++class cMenuSetupLiveBuffer : public cMenuSetupBase {
++private:
++ void Setup();
++public:
++ eOSState ProcessKey(eKeys Key);
++ cMenuSetupLiveBuffer(void);
++ };
++
++cMenuSetupLiveBuffer::cMenuSetupLiveBuffer(void)
++{
++ SetSection("permanentes Timeshift");
++ Setup();
+ }
+
++void cMenuSetupLiveBuffer::Setup(void)
++{
++ int current=Current();
++ Clear();
++ Add(new cMenuEditBoolItem(tr("Permanent Timeshift"), &data.LiveBuffer));
++ if (data.LiveBuffer) {
++ Add(new cMenuEditBoolItem(tr("Setup.LiveBuffer$Location"), &data.InRAM,tr("Setup.LiveBuffer$harddisk"),tr("Setup.LiveBuffer$memory")));
++ if (data.InRAM) {
++ Add(new cMenuEditIntItem( tr("Setup.LiveBuffer$Buffer size in memory (MB)"), &data.MemBufSize, 5, 1000));
++ Add(new cMenuEditBoolItem(tr("Setup.LiveBuffer$Extend to harddisk if necessary"), &data.ExtendBuffer));
++ Add(new cMenuEditBoolItem(tr("Setup.LiveBuffer$Keep paused Buffer"), &data.KeepPaused));
++ if (data.ExtendBuffer || data.KeepPaused)
++ Add(new cMenuEditIntItem(tr("Setup.LiveBuffer$Buffer size on harddisk (MB)"), &data.LiveBufferSize, 1, 100000));
++ }
++ else {
++ Add(new cMenuEditIntItem( tr("Setup.LiveBuffer$Buffer size (MB)"), &data.LiveBufferSize, 1, 100000));
++ Add(new cMenuEditBoolItem(tr("Setup.LiveBuffer$Keep paused Buffer"), &data.KeepPaused));
++ }
++ }
++ SetCurrent(Get(current));
++ Display();
++}
++
++eOSState cMenuSetupLiveBuffer::ProcessKey(eKeys Key)
++{
++ int oldLiveBuffer = data.LiveBuffer;
++ int oldInRAM = data.InRAM;
++ int oldExtendBuffer = data.ExtendBuffer;
++ int oldKeepPaused = data.KeepPaused;
++ eOSState state = cMenuSetupBase::ProcessKey(Key);
++
++ if (Key != kNone && (data.LiveBuffer != oldLiveBuffer || data.InRAM != oldInRAM || data.ExtendBuffer != oldExtendBuffer || data.KeepPaused != oldKeepPaused))
++ Setup();
++ return state;
++}
++
++#endif /* LIVEBUFFER */
+ // --- cMenuSetupPluginItem --------------------------------------------------
+
+ class cMenuSetupPluginItem : public cOsdItem {
+@@ -2826,6 +3989,9 @@ class cMenuSetupPlugins : public cMenuSe
+ public:
+ cMenuSetupPlugins(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupPlugins"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupPlugins::cMenuSetupPlugins(void)
+@@ -2875,6 +4041,9 @@ private:
+ public:
+ cMenuSetup(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetup"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetup::cMenuSetup(void)
+@@ -2898,6 +4067,9 @@ void cMenuSetup::Set(void)
+ Add(new cOsdItem(hk(tr("Recording")), osUser6));
+ Add(new cOsdItem(hk(tr("Replay")), osUser7));
+ Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8));
++#ifdef USE_LIVEBUFFER
++ Add(new cOsdItem(hk(tr("Permanent Timeshift")),osLiveBuffer));
++#endif /* LIVEBUFFER */
+ if (cPluginManager::HasPlugins())
+ Add(new cOsdItem(hk(tr("Plugins")), osUser9));
+ Add(new cOsdItem(hk(tr("Restart")), osUser10));
+@@ -2928,6 +4100,9 @@ eOSState cMenuSetup::ProcessKey(eKeys Ke
+ case osUser8: return AddSubMenu(new cMenuSetupMisc);
+ case osUser9: return AddSubMenu(new cMenuSetupPlugins);
+ case osUser10: return Restart();
++#ifdef USE_LIVEBUFFER
++ case osLiveBuffer: return AddSubMenu(new cMenuSetupLiveBuffer);
++#endif /* LIVEBUFFER */
+ default: ;
+ }
+ if (I18nCurrentLanguage() != osdLanguage) {
+@@ -2964,24 +4139,91 @@ cOsdObject *cMenuMain::pluginOsdObject =
+ cMenuMain::cMenuMain(eOSState State)
+ :cOsdMenu("")
+ {
++#ifdef USE_SETUP
++ // Load Menu Configuration
++ char *menuXML = NULL;
++ asprintf(&menuXML, "%s/setup/vdr-menu.%s.xml", cPlugin::ConfigDirectory(), Setup.OSDLanguage);
++ if (access(menuXML, 04) == -1)
++ asprintf(&menuXML, "%s/setup/vdr-menu.xml", cPlugin::ConfigDirectory());
++ subMenu.LoadXml(menuXML);
++ free(menuXML);
++ nrDynamicMenuEntries=0;
++#endif /* SETUP */
+ replaying = false;
+ stopReplayItem = NULL;
+ cancelEditingItem = NULL;
+ stopRecordingItem = NULL;
+ recordControlsState = 0;
++
++#ifdef USE_MENUORG
++ MenuOrgPatch::EnterRootMenu();
++#endif /* MENUORG */
++
+ Set();
+
+ // Initial submenus:
+
++#ifdef USE_MAINMENUHOOKS
++ cOsdMenu *menu = NULL;
++#endif /* MAINMENUHOOKS */
+ switch (State) {
++#ifdef USE_MAINMENUHOOKS
++ case osSchedule:
++ {
++ cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", &menu);
++ if (p && !menu)
++ isyslog("MainMenuHook::osSchedule: plugin %s claims to support service but didn't return menu", p->Name());
++
++ if (!menu)
++ menu = new cMenuSchedule;
++ }
++ break;
++ case osChannels:
++ {
++ cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", &menu);
++ if (p && !menu)
++ isyslog("MainMenuHook::osChannels: plugin %s claims to support service but didn't return menu", p->Name());
++
++ if (!menu)
++ menu = new cMenuChannels;
++ }
++ break;
++ case osTimers:
++ {
++ cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu);
++ if (p && !menu)
++ isyslog("MainMenuHook::osTimers: plugin %s claims to support service but didn't return menu", p->Name());
++
++ if (!menu)
++ menu = new cMenuTimers;
++ }
++ break;
++ case osRecordings:
++ {
++ cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", &menu);
++ if (p && !menu)
++ isyslog("MainMenuHook::osRecordings: plugin %s claims to support service but didn't return menu", p->Name());
++
++ if (!menu)
++ menu = new cMenuRecordings(NULL, 0, true);
++ }
++ break;
++ case osSetup: menu = new cMenuSetup; break;
++ case osCommands: menu = new cMenuCommands(tr("Commands"), &Commands); break;
++#else
+ case osSchedule: AddSubMenu(new cMenuSchedule); break;
+ case osChannels: AddSubMenu(new cMenuChannels); break;
+ case osTimers: AddSubMenu(new cMenuTimers); break;
+ case osRecordings: AddSubMenu(new cMenuRecordings(NULL, 0, true)); break;
+ case osSetup: AddSubMenu(new cMenuSetup); break;
+ case osCommands: AddSubMenu(new cMenuCommands(tr("Commands"), &Commands)); break;
++#endif /* MAINMENUHOOKS */
+ default: break;
+ }
++#ifdef USE_MAINMENUHOOKS
++ if (menu)
++ AddSubMenu(menu);
++#endif /* MAINMENUHOOKS */
+ }
+
+ cOsdObject *cMenuMain::PluginOsdObject(void)
+@@ -2991,38 +4233,155 @@ cOsdObject *cMenuMain::PluginOsdObject(v
+ return o;
+ }
+
++#ifdef USE_SETUP
++void cMenuMain::Set(int current)
++#else
+ void cMenuMain::Set(void)
++#endif /* SETUP */
+ {
+ Clear();
+ SetTitle("VDR");
+ SetHasHotkeys();
+
++#ifdef USE_MENUORG
++ if (MenuOrgPatch::IsCustomMenuAvailable()) {
++ MenuItemDefinitions* menuItems = MenuOrgPatch::MainMenuItems();
++ for (MenuItemDefinitions::iterator i = menuItems->begin(); i != menuItems->end(); i++) {
++ cOsdItem* osdItem = NULL;
++ if ((*i)->IsCustomOsdItem()) {
++ osdItem = (*i)->CustomOsdItem();
++ if (osdItem && !(*i)->IsSeparatorItem())
++ osdItem->SetText(hk(osdItem->Text()));
++ }
++ else if ((*i)->IsPluginItem()) {
++ const char *item = (*i)->PluginMenuEntry();
++ if (item)
++ osdItem = new cMenuPluginItem(hk(item), (*i)->PluginIndex());
++ }
++ if (osdItem) {
++ Add(osdItem);
++ if ((*i)->IsSelected())
++ SetCurrent(osdItem);
++ }
++ }
++ }
++ else {
++#endif /* MENUORG */
++
++#ifdef USE_SETUP
++ stopReplayItem = NULL;
++ cancelEditingItem = NULL;
++ stopRecordingItem = NULL;
++
++ // remember initial dynamic MenuEntries added
++ nrDynamicMenuEntries = Count();
++ for (cSubMenuNode *node = subMenu.GetMenuTree()->First(); node; node = subMenu.GetMenuTree()->Next(node)) {
++ cSubMenuNode::Type type = node->GetType();
++ if (type==cSubMenuNode::PLUGIN) {
++ const char *item = node->GetPluginMainMenuEntry();
++#ifdef USE_PINPLUGIN
++ if (item && !cStatus::MsgPluginProtected(cPluginManager::GetPlugin(node->GetPluginIndex()), true))
++#else
++ if (item)
++#endif /* PINPLUGIN */
++ Add(new cMenuPluginItem(hk(item), node->GetPluginIndex()));
++ }
++ else if (type==cSubMenuNode::MENU) {
++ cString item = cString::sprintf("%s%s", node->GetName(), subMenu.GetMenuSuffix());
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgMenuItemProtected(item, true))
++ Add(new cOsdItem(hk(item), osUnknown, node));
++#else
++ Add(new cOsdItem(hk(item)));
++#endif /* PINPLUGIN */
++ }
++ else if ((type==cSubMenuNode::COMMAND) || (type==cSubMenuNode::THREAD)) {
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgMenuItemProtected(node->GetName(), true))
++ Add(new cOsdItem(hk(node->GetName()), osUnknown, node));
++#else
++ Add(new cOsdItem(hk(node->GetName())));
++#endif /* PINPLUGIN */
++ }
++ else if (type==cSubMenuNode::SYSTEM) {
++ const char *item = node->GetName();
++#ifdef USE_PINPLUGIN
++ if (cStatus::MsgMenuItemProtected(item, true))
++ ; // nothing to do ;)
++ else
++#endif /* PINPLUGIN */
++ if (strcmp(item, "Schedule") == 0)
++ Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
++ else if (strcmp(item, "Channels") == 0)
++ Add(new cOsdItem(hk(tr("Channels")), osChannels));
++ else if (strcmp(item, "Timers") == 0)
++ Add(new cOsdItem(hk(tr("Timers")), osTimers));
++ else if (strcmp(item, "Recordings") == 0)
++ Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++ else if (strcmp(item, "Setup") == 0)
++ Add(new cOsdItem(hk(tr("Setup")), osSetup));
++ else if (strcmp(item, "Commands") == 0 && Commands.Count()>0)
++ Add(new cOsdItem(hk(tr("Commands")), osCommands));
++ }
++ }
++ if (current >=0 && current<Count()) {
++ SetCurrent(Get(current));
++ }
++
++#else /* NO SETUP */
++
+ // Basic menu items:
+
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgMenuItemProtected("Schedule", true)) Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
++ if (!cStatus::MsgMenuItemProtected("Channels", true)) Add(new cOsdItem(hk(tr("Channels")), osChannels));
++ if (!cStatus::MsgMenuItemProtected("Timers", true)) Add(new cOsdItem(hk(tr("Timers")), osTimers));
++ if (!cStatus::MsgMenuItemProtected("Recordings", true)) Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++#else
+ Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
+ Add(new cOsdItem(hk(tr("Channels")), osChannels));
+ Add(new cOsdItem(hk(tr("Timers")), osTimers));
+ Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++#endif /* PINPLUGIN */
+
+ // Plugins:
+
+ for (int i = 0; ; i++) {
+ cPlugin *p = cPluginManager::GetPlugin(i);
+ if (p) {
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgPluginProtected(p, true)) {
++#endif /* PINPLUGIN */
+ const char *item = p->MainMenuEntry();
+ if (item)
+ Add(new cMenuPluginItem(hk(item), i));
+ }
++#ifdef USE_PINPLUGIN
++ }
++#endif /* PINPLUGIN */
+ else
+ break;
+ }
+
+ // More basic menu items:
+
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgMenuItemProtected("Setup", true)) Add(new cOsdItem(hk(tr("Setup")), osSetup));
++#else
+ Add(new cOsdItem(hk(tr("Setup")), osSetup));
++#endif /* PINPLUGIN */
+ if (Commands.Count())
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgMenuItemProtected("Commands", true))
++#endif /* PINPLUGIN */
+ Add(new cOsdItem(hk(tr("Commands")), osCommands));
+
++#endif /* SETUP */
++
++#ifdef USE_MENUORG
++ }
++#endif /* MENUORG */
++
+ Update(true);
+
+ Display();
+@@ -3032,12 +4391,40 @@ bool cMenuMain::Update(bool Force)
+ {
+ bool result = false;
+
++#ifdef USE_SETUP
++ cOsdItem *fMenu = NULL;
++ if (Force && subMenu.isTopMenu()) {
++ fMenu = First();
++ nrDynamicMenuEntries = 0;
++ }
++
++ if (subMenu.isTopMenu()) {
++#endif /* SETUP */
++#ifdef USE_LIEMIEXT
++// this extension is not included in the original Liemikuutio
++ if (Setup.MainMenuTitle) {
++ if (Setup.MainMenuTitle == 1)
++ SetTitle(cString::sprintf("%s - %s", tr("VDR"), Setup.CustomMainMenuTitle));
++ else if (Setup.MainMenuTitle == 2)
++ SetTitle(cString::sprintf("%s", Setup.CustomMainMenuTitle));
++ else if (Setup.MainMenuTitle == 3)
++ SetTitle(cString::sprintf("%s %s", tr("VDR"), VDRVERSION));
++ }
++ else
++#endif /* LIEMIEXT */
+ // Title with disk usage:
+ if (FreeDiskSpace.HasChanged(Force)) {
+ //XXX -> skin function!!!
+ SetTitle(cString::sprintf("%s - %s", tr("VDR"), FreeDiskSpace.FreeDiskSpaceString()));
+ result = true;
+ }
++#ifdef USE_SETUP
++ }
++ else {
++ SetTitle(cString::sprintf("%s - %s", tr("VDR"), subMenu.GetParentMenuTitel()));
++ result = true;
++ }
++#endif /* SETUP */
+
+ bool NewReplaying = cControl::Control() != NULL;
+ if (Force || NewReplaying != replaying) {
+@@ -3045,6 +4432,9 @@ bool cMenuMain::Update(bool Force)
+ // Replay control:
+ if (replaying && !stopReplayItem)
+ // TRANSLATORS: note the leading blank!
++#ifdef USE_LIEMIEXT
++ if (Setup.MenuCmdPosition) Ins(stopReplayItem = new cOsdItem(tr(" Stop replaying"), osStopReplay)); else
++#endif /* LIEMIEXT */
+ Add(stopReplayItem = new cOsdItem(tr(" Stop replaying"), osStopReplay));
+ else if (stopReplayItem && !replaying) {
+ Del(stopReplayItem->Index());
+@@ -3059,6 +4449,9 @@ bool cMenuMain::Update(bool Force)
+ bool CutterActive = cCutter::Active();
+ if (CutterActive && !cancelEditingItem) {
+ // TRANSLATORS: note the leading blank!
++#ifdef USE_LIEMIEXT
++ if (Setup.MenuCmdPosition) Ins(cancelEditingItem = new cOsdItem(tr(" Cancel editing"), osCancelEdit)); else
++#endif /* LIEMIEXT */
+ Add(cancelEditingItem = new cOsdItem(tr(" Cancel editing"), osCancelEdit));
+ result = true;
+ }
+@@ -3079,6 +4472,9 @@ bool cMenuMain::Update(bool Force)
+ while ((s = cRecordControls::GetInstantId(s)) != NULL) {
+ cOsdItem *item = new cOsdItem(osStopRecord);
+ item->SetText(cString::sprintf("%s%s", tr(STOP_RECORDING), s));
++#ifdef USE_LIEMIEXT
++ if (Setup.MenuCmdPosition) Ins(item); else
++#endif /* LIEMIEXT */
+ Add(item);
+ if (!stopRecordingItem)
+ stopRecordingItem = item;
+@@ -3086,6 +4482,12 @@ bool cMenuMain::Update(bool Force)
+ result = true;
+ }
+
++#ifdef USE_SETUP
++ // adjust nrDynamicMenuEntries
++ if (fMenu != NULL)
++ nrDynamicMenuEntries = fMenu->Index();
++#endif /* SETUP */
++
+ return result;
+ }
+
+@@ -3096,13 +4498,69 @@ eOSState cMenuMain::ProcessKey(eKeys Key
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ HadSubMenu |= HasSubMenu();
+
++#ifdef USE_PINPLUGIN
++ cOsdItem* item = Get(Current());
++
++ if (item && item->Text() && state != osBack && state != osContinue && Key != kNone)
++ if (cStatus::MsgMenuItemProtected(item->Text()))
++ return osContinue;
++#endif /* PINPLUGIN */
++
++#ifdef USE_MAINMENUHOOKS
++ cOsdMenu *menu = NULL;
++#endif /* MAINMENUHOOKS */
+ switch (state) {
++#ifdef USE_MAINMENUHOOKS
++ case osSchedule:
++ {
++ cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", &menu);
++ if (p && !menu)
++ isyslog("MainMenuHook::osSchedule: plugin %s claims to support service but didn't return menu", p->Name());
++
++ if (!menu)
++ menu = new cMenuSchedule;
++ }
++ break;
++ case osChannels:
++ {
++ cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", &menu);
++ if (p && !menu)
++ isyslog("MainMenuHook::osChannels: plugin %s claims to support service but didn't return menu", p->Name());
++
++ if (!menu)
++ menu = new cMenuChannels;
++ }
++ break;
++ case osTimers:
++ {
++ cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu);
++ if (p && !menu)
++ isyslog("MainMenuHook::osTimers: plugin %s claims to support service but didn't return menu", p->Name());
++
++ if (!menu)
++ menu = new cMenuTimers;
++ }
++ break;
++ case osRecordings:
++ {
++ cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", &menu);
++ if (p && !menu)
++ isyslog("MainMenuHook::osRecordings: plugin %s claims to support service but didn't return menu", p->Name());
++
++ if (!menu)
++ menu = new cMenuRecordings;
++ }
++ break;
++ case osSetup: menu = new cMenuSetup; break;
++ case osCommands: menu = new cMenuCommands(tr("Commands"), &Commands); break;
++#else
+ case osSchedule: return AddSubMenu(new cMenuSchedule);
+ case osChannels: return AddSubMenu(new cMenuChannels);
+ case osTimers: return AddSubMenu(new cMenuTimers);
+ case osRecordings: return AddSubMenu(new cMenuRecordings);
+ case osSetup: return AddSubMenu(new cMenuSetup);
+ case osCommands: return AddSubMenu(new cMenuCommands(tr("Commands"), &Commands));
++#endif /* MAINMENUHOOKS */
+ case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) {
+ cOsdItem *item = Get(Current());
+ if (item) {
+@@ -3121,6 +4579,9 @@ eOSState cMenuMain::ProcessKey(eKeys Key
+ if (item) {
+ cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
+ if (p) {
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgPluginProtected(p)) {
++#endif /* PINPLUGIN */
+ cOsdObject *menu = p->MainMenuAction();
+ if (menu) {
+ if (menu->IsMenu())
+@@ -3132,9 +4593,60 @@ eOSState cMenuMain::ProcessKey(eKeys Key
+ }
+ }
+ }
++#ifdef USE_PINPLUGIN
++ }
++#endif /* PINPLUGIN */
+ state = osEnd;
+ }
+ break;
++#ifdef USE_SETUP
++ case osBack: {
++ int newCurrent = 0;
++ if (subMenu.Up(&newCurrent)) {
++ Set(newCurrent);
++ return osContinue;
++ }
++ else
++ return osEnd;
++ }
++ break;
++#endif /* SETUP */
++#ifdef USE_MENUORG
++ case osBack: {
++ if (MenuOrgPatch::IsCustomMenuAvailable()) {
++ bool leavingMenuSucceeded = MenuOrgPatch::LeaveSubMenu();
++ Set();
++ stopReplayItem = NULL;
++ cancelEditingItem = NULL;
++ stopRecordingItem = NULL;
++ recordControlsState = 0;
++ Update(true);
++ Display();
++ if (leavingMenuSucceeded)
++ return osContinue;
++ else
++ return osEnd;
++ }
++ }
++ break;
++ case osUser1: {
++ if (MenuOrgPatch::IsCustomMenuAvailable()) {
++ MenuOrgPatch::EnterSubMenu(Get(Current()));
++ Set();
++ return osContinue;
++ }
++ }
++ break;
++ case osUser2: {
++ if (MenuOrgPatch::IsCustomMenuAvailable()) {
++ cOsdMenu* osdMenu = MenuOrgPatch::Execute(Get(Current()));
++ if (osdMenu)
++ return AddSubMenu(osdMenu);
++ return osEnd;
++ }
++ }
++ break;
++#endif /* MENUORG */
+ default: switch (Key) {
+ case kRecord:
+ case kRed: if (!HadSubMenu)
+@@ -3151,9 +4663,67 @@ eOSState cMenuMain::ProcessKey(eKeys Key
+ case kBlue: if (!HadSubMenu)
+ state = replaying ? osStopReplay : cReplayControl::LastReplayed() ? osReplay : osContinue;
+ break;
++#ifdef USE_SETUP
++ case kOk: if (state == osUnknown) {
++ cString buffer;
++#ifdef USE_PINPLUGIN
++ cSubMenuNode *node = Get(Current())->SubMenu();
++#else
++ int index = Current()-nrDynamicMenuEntries;
++ cSubMenuNode *node = subMenu.GetNode(index);
++#endif /* PINPLUGIN */
++
++ if (node != NULL) {
++ if (node->GetType() == cSubMenuNode::MENU) {
++#ifdef USE_PINPLUGIN
++ subMenu.Down(node, Current());
++#else
++ subMenu.Down(index);
++#endif /* PINPLUGIN */
++ }
++ else if (node->GetType() == cSubMenuNode::COMMAND) {
++ bool confirmed = true;
++ if (node->CommandConfirm()) {
++ buffer = cString::sprintf("%s?", node->GetName());
++ confirmed = Interface->Confirm(buffer);
++ }
++ if (confirmed) {
++ const char *Result = subMenu.ExecuteCommand(node->GetCommand());
++ if (Result)
++ return AddSubMenu(new cMenuText(node->GetName(), Result, fontFix));
++ return osEnd;
++ }
++ }
++ else if (node->GetType() == cSubMenuNode::THREAD) {
++ bool confirmed = true;
++ if (node->CommandConfirm()) {
++ buffer = cString::sprintf("%s?", node->GetName());
++ confirmed = Interface->Confirm(buffer);
++ }
++ if (confirmed) {
++ buffer = cString::sprintf("%s", node->GetCommand());
++ cExecCmdThread *execcmd = new cExecCmdThread(node->GetCommand());
++ if (execcmd->Start())
++ dsyslog("executing command '%s'", *buffer);
++ else
++ esyslog("ERROR: can't execute command '%s'", *buffer);
++ return osEnd;
++ }
++ }
++ }
++
++ Set();
++ return osContinue;
++ }
++ break;
++#endif /* SETUP */
+ default: break;
+ }
+ }
++#ifdef USE_MAINMENUHOOKS
++ if (menu)
++ return AddSubMenu(menu);
++#endif /* MAINMENUHOOKS */
+ if (!HasSubMenu() && Update(HadSubMenu))
+ Display();
+ if (Key != kNone) {
+@@ -3297,7 +4867,14 @@ cChannel *cDisplayChannel::NextAvailable
+ if (Direction) {
+ while (Channel) {
+ Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
++#ifdef USE_PINPLUGIN
++ if (cStatus::MsgChannelProtected(0, Channel) == false)
++#endif /* PINPLUGIN */
++#ifdef USE_LNBSHARE
++ if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true) && cDevice::PrimaryDevice()->GetMaxBadPriority(Channel) < 0)
++#else
+ if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true))
++#endif /* LNBSHARE */
+ return Channel;
+ }
+ }
+@@ -3355,6 +4932,13 @@ eOSState cDisplayChannel::ProcessKey(eKe
+ case kLeft:
+ case kRight|k_Repeat:
+ case kRight:
++#ifdef USE_VOLCTRL
++ if (Setup.LRVolumeControl && !Setup.LRChannelGroups) {
++ cRemote::Put(NORMALKEY(Key) == kLeft ? kVolDn : kVolUp, true);
++ break;
++ }
++ // else fall through
++#endif /* VOLCTRL */
+ case kNext|k_Repeat:
+ case kNext:
+ case kPrev|k_Repeat:
+@@ -3514,6 +5098,17 @@ void cDisplayVolume::Process(eKeys Key)
+ eOSState cDisplayVolume::ProcessKey(eKeys Key)
+ {
+ switch (Key) {
++#ifdef USE_VOLCTRL
++ case kLeft|k_Repeat:
++ case kLeft:
++ case kRight|k_Repeat:
++ case kRight:
++ if (Setup.LRVolumeControl) {
++ cRemote::Put(NORMALKEY(Key) == kLeft ? kVolDn : kVolUp, true);
++ break;
++ }
++ // else fall through
++#endif /* VOLCTRL */
+ case kVolUp|k_Repeat:
+ case kVolUp:
+ case kVolDn|k_Repeat:
+@@ -3763,6 +5358,10 @@ eOSState cDisplaySubtitleTracks::Process
+
+ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
+ {
++#ifdef USE_DVLRECSCRIPTADDON
++ const cChannel *recChan = NULL;
++ char *chanName = NULL;
++#endif /* DVLRECSCRIPTADDON */
+ // We're going to manipulate an event here, so we need to prevent
+ // others from modifying any EPG data:
+ cSchedulesLock SchedulesLock;
+@@ -3807,12 +5406,57 @@ cRecordControl::cRecordControl(cDevice *
+ return;
+ }
+
++#ifdef USE_DVLRECSCRIPTADDON
++ if (timer)
++ if ((recChan = timer->Channel()) != NULL)
++ chanName = strdup(recChan->Name());
++ if (chanName != NULL) {
++ cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName, chanName);
++ free(chanName);
++ }
++ else
++#endif /* DVLRECSCRIPTADDON */
+ cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
+ isyslog("record %s", fileName);
+ if (MakeDirs(fileName, true)) {
+ const cChannel *ch = timer->Channel();
++#if defined (USE_LIVEBUFFER) || defined (USE_TTXTSUBS)
++#ifdef USE_LIVEBUFFER
++ int startFrame=-1;
++ int endFrame=0;
++ cLiveBuffer *liveBuffer = cLiveBufferManager::InLiveBuffer(timer, &startFrame, &endFrame);
++ if (liveBuffer) {
++ timer->SetFlags(tfhasLiveBuf);
++ liveBuffer->SetStartFrame(startFrame);
++ if (endFrame) {
++ liveBuffer->CreateIndexFile(fileName, 0, endFrame);
++ Timers.Del(timer);
++ Timers.SetModified();
++ timer = NULL;
++ Recording.WriteInfo();
++ Recordings.AddByName(fileName);
++ return;
++ }
++ }
++#endif /* LIVEBUFFER */
++#ifdef USE_TTXTSUBS
++ cTtxtSubsRecorderBase *subsRecorder = cVDRTtxtsubsHookListener::Hook()->NewTtxtSubsRecorder(device, ch);
++#endif /* TTXTSUBS */
++ recorder = new cRecorder(fileName, ch->GetChannelID(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids()
++#ifdef USE_TTXTSUBS
++ ,subsRecorder
++#endif /* TTXTSUBS */
++#ifdef USE_LIVEBUFFER
++ ,liveBuffer
++#endif /* LIVEBUFFER */
++ );
++#else
+ recorder = new cRecorder(fileName, ch->GetChannelID(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids());
++#endif /* LIVEBUFFER || TTXTSUBS */
+ if (device->AttachReceiver(recorder)) {
++#ifdef USE_TTXTSUBS
++ if (subsRecorder) subsRecorder->DeviceAttach();
++#endif /* TTXTSUBS */
+ Recording.WriteInfo();
+ cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
+ if (!Timer && !cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo()
+@@ -3869,11 +5513,25 @@ bool cRecordControl::GetEvent(void)
+ void cRecordControl::Stop(void)
+ {
+ if (timer) {
++#ifdef USE_DVLRECSCRIPTADDON
++ char *chanName = NULL;
++ const cChannel *recChan = NULL;
++
++ recChan = timer -> Channel();
++ if (recChan != NULL)
++ chanName = strdup(recChan -> Name());
++#endif /* DVLRECSCRIPTADDON */
+ DELETENULL(recorder);
+ timer->SetRecording(false);
+ timer = NULL;
+ cStatus::MsgRecording(device, NULL, fileName, false);
++#ifdef USE_DVLRECSCRIPTADDON
++ cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName, chanName);
++ if (chanName != NULL)
++ free(chanName);
++#else
+ cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName);
++#endif /* DVLRECSCRIPTADDON */
+ }
+ }
+
+@@ -3920,15 +5578,35 @@ bool cRecordControls::Start(cTimer *Time
+ int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority;
+ cDevice *device = cDevice::GetDevice(channel, Priority, false);
+ if (device) {
++#ifdef USE_LNBSHARE
++ cDevice *tmpDevice;
++ while ((tmpDevice = device->GetBadDevice(channel))) {
++ if (tmpDevice->Replaying() == false) {
++// Stop(tmpDevice);
++ if (tmpDevice->IsPrimaryDevice() )
++ tmpDevice->SwitchChannelForced(channel, true);
++ else
++ tmpDevice->SwitchChannelForced(channel, false);
++ } else
++ tmpDevice->SwitchChannelForced(channel, false);
++ }
++#endif /* LNBSHARE */
+ dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number());
+ if (!device->SwitchChannel(channel, false)) {
+ ShutdownHandler.RequestEmergencyExit();
+ return false;
+ }
++#ifdef USE_LIVEBUFFER
++ if (!Timer || Timer->Matches() || cLiveBufferManager::InLiveBuffer(Timer)) {
++#else
+ if (!Timer || Timer->Matches()) {
++#endif /* LIVEBUFFER */
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
+ if (!RecordControls[i]) {
+ RecordControls[i] = new cRecordControl(device, Timer, Pause);
++#ifdef USE_PINPLUGIN
++ cStatus::MsgRecordingFile(RecordControls[i]->FileName());
++#endif /* PINPLUGIN */
+ return RecordControls[i]->Process(time(NULL));
+ }
+ }
+@@ -4059,12 +5737,22 @@ bool cRecordControls::StateChanged(int &
+
+ // --- cReplayControl --------------------------------------------------------
+
++#ifdef USE_LIEMIEXT
++#define REPLAYCONTROLSKIPLIMIT 9 // s
++#define REPLAYCONTROLSKIPSECONDS 90 // s
++#define REPLAYCONTROLSKIPTIMEOUT 5000 // ms
++#endif /* LIEMIEXT */
++
+ cReplayControl *cReplayControl::currentReplayControl = NULL;
+ char *cReplayControl::fileName = NULL;
+ char *cReplayControl::title = NULL;
+
+ cReplayControl::cReplayControl(void)
++#ifdef USE_JUMPPLAY
++:cDvbPlayerControl(fileName), marks(fileName)
++#else
+ :cDvbPlayerControl(fileName)
++#endif /* JUMPPLAY */
+ {
+ currentReplayControl = this;
+ displayReplay = NULL;
+@@ -4072,10 +5760,28 @@ cReplayControl::cReplayControl(void)
+ lastCurrent = lastTotal = -1;
+ lastPlay = lastForward = false;
+ lastSpeed = -2; // an invalid value
++#ifdef USE_LIEMIEXT
++ lastSkipKey = kNone;
++ lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
++ lastSkipTimeout.Set(0);
++#endif /* LIEMIEXT */
+ timeoutShow = 0;
+ timeSearchActive = false;
++#ifndef USE_JUMPPLAY
+ marks.Load(fileName);
++#endif /* JUMPPLAY */
+ cRecording Recording(fileName);
++#ifdef USE_DVDARCHIVE
++ canJumpChapters = (Recording.GetDvdType() == DVD_VIDEO_ARCHIVE_TYPE);
++ dvdchapters = NULL;
++ if (canJumpChapters) {
++ const char *ret = Recording.GetDvdChapters();
++ if (ret)
++ dvdchapters = strdup(ret);
++ else
++ canJumpChapters=false;
++ }
++#endif /* DVDARCHIVE */
+ cStatus::MsgReplaying(this, Recording.Name(), Recording.FileName(), true);
+ SetTrackDescriptions(false);
+ }
+@@ -4083,12 +5789,67 @@ cReplayControl::cReplayControl(void)
+ cReplayControl::~cReplayControl()
+ {
+ Hide();
++#ifdef USE_DVDARCHIVE
++ free(dvdchapters);
++#endif /* DVDARCHIVE */
+ cStatus::MsgReplaying(this, NULL, fileName, false);
+ Stop();
+ if (currentReplayControl == this)
+ currentReplayControl = NULL;
+ }
+
++#ifdef USE_DELTIMESHIFTREC
++void cReplayControl::Stop(void)
++{
++ int dummy;
++ bool playing = GetIndex(dummy, dummy, false);
++ cRecordControl* rc = cRecordControls::GetRecordControl(fileName);
++
++ if (playing && rc && rc->InstantId()) {
++ isyslog("found Timeshiftrecording");
++
++ if ((Setup.DelTimeshiftRec != 0 ) || (Interface->Confirm(tr("Delete recording?")))) {
++ cRecordControl *rc = cRecordControls::GetRecordControl(fileName);
++ if (rc) {
++ cTimer *timer = rc->Timer();
++ if (timer) {
++ const char* reccmd_backup = cRecordingUserCommand::GetCommand();
++ cRecordingUserCommand::SetCommand(NULL);
++
++ timer->Skip();
++ cRecordControls::Process(time(NULL));
++ if (timer->IsSingleEvent()) {
++ isyslog("deleting timer %s", *timer->ToDescr());
++ Timers.Del(timer);
++ }
++ Timers.SetModified();
++
++ // restore reccmd
++ cRecordingUserCommand::SetCommand(reccmd_backup);
++ }
++ }
++ isyslog("stop replaying %s", fileName);
++ cDvbPlayerControl::Stop();
++
++ if (Setup.DelTimeshiftRec != 1) {
++ cRecording *recording = Recordings.GetByName(fileName);;
++ if (recording) {
++ if (recording->Delete()) {
++ Recordings.DelByName(fileName);
++ ClearLastReplayed(fileName);
++ return;
++ }
++ else
++ Skins.Message(mtError, tr("Error while deleting recording!"));
++ }
++ }
++ }
++ else
++ cDvbPlayerControl::Stop();
++ }
++}
++#endif /* DELTIMESHIFTREC */
++
+ void cReplayControl::SetRecording(const char *FileName, const char *Title)
+ {
+ free(fileName);
+@@ -4183,6 +5944,9 @@ bool cReplayControl::ShowProgress(bool I
+ if (Initial) {
+ if (title)
+ displayReplay->SetTitle(title);
++#ifdef USE_OSDMAXITEMS
++ displayReplay->SetButtons(tr("Jump"), tr("Skip +60s"), tr("Skip -60s"), tr("Button$Stop"));
++#endif /* OSDMAXITEMS */
+ lastCurrent = lastTotal = -1;
+ }
+ if (Total != lastTotal) {
+@@ -4304,8 +6068,15 @@ void cReplayControl::MarkToggle(void)
+ ShowTimed(2);
+ bool Play, Forward;
+ int Speed;
++#ifdef USE_JUMPPLAY
++ if (GetReplayMode(Play, Forward, Speed) && !Play) {
++ Goto(Current, true);
++ displayFrames = true;
++ }
++#else
+ if (GetReplayMode(Play, Forward, Speed) && !Play)
+ Goto(Current, true);
++#endif /* JUMPPLAY */
+ }
+ marks.Save();
+ }
+@@ -4318,8 +6089,22 @@ void cReplayControl::MarkJump(bool Forwa
+ if (GetIndex(Current, Total)) {
+ cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current);
+ if (m) {
++#ifdef USE_JUMPPLAY
++ bool Play2, Forward2;
++ int Speed;
++ if (Setup.JumpPlay && GetReplayMode(Play2, Forward2, Speed) &&
++ Play2 && Forward && m->position < Total - SecondsToFrames(3)) {
++ Goto(m->position);
++ Play();
++ }
++ else {
++ Goto(m->position, true);
++ displayFrames = true;
++ }
++#else
+ Goto(m->position, true);
+ displayFrames = true;
++#endif /* JUMPPLAY */
+ }
+ }
+ }
+@@ -4348,11 +6133,43 @@ void cReplayControl::MarkMove(bool Forwa
+ }
+ }
+
++#ifdef USE_DVDARCHIVE
++void cReplayControl::ChaptersJump(bool Forward)
++{
++ int Current, Total;
++ if (GetIndex(Current, Total)) {
++ int position = -1;
++ char *buf, *pos;
++ cString old1("-1");
++ cString old2("-1");
++ buf = strdup(dvdchapters);
++ pos = strtok(buf, ",");
++ while (pos != NULL && position == -1) {
++ if (pos && atoi(pos) > Current)
++ position = Forward ? atoi(pos) : ((Current - atoi(old1)) <= (3*FRAMESPERSEC)) ? atoi(old2) : atoi(old1);
++ old2 = old1;
++ old1 = strdup(pos);
++ if(position == -1) pos = strtok(NULL, ",");
++ }
++ if (!pos && !Forward)
++ position = ((Current - atoi(old1)) <= (3*FRAMESPERSEC)) ? atoi(old2) : atoi(old1);
++ if (position >= 0) {
++ Goto(position);
++ Play();
++ }
++ }
++}
++
++#endif /* DVDARCHIVE */
+ void cReplayControl::EditCut(void)
+ {
+ if (fileName) {
+ Hide();
++#ifdef USE_CUTTERQUEUE
++ if (!cCutter::Active() || Interface->Confirm(tr("Cutter already running - Add to cutting queue?"))) {
++#else
+ if (!cCutter::Active()) {
++#endif /* CUTTERQUEUE */
+ if (!marks.Count())
+ Skins.Message(mtError, tr("No editing marks defined!"));
+ else if (!cCutter::Start(fileName))
+@@ -4374,7 +6191,11 @@ void cReplayControl::EditTest(void)
+ if (!m)
+ m = marks.GetNext(Current);
+ if (m) {
++#ifdef USE_JUMPPLAY
++ if ((m->Index() & 0x01) != 0 && !Setup.PlayJump)
++#else
+ if ((m->Index() & 0x01) != 0)
++#endif /* JUMPPLAY */
+ m = marks.Next(m);
+ if (m) {
+ Goto(m->position - SecondsToFrames(3));
+@@ -4396,6 +6217,9 @@ eOSState cReplayControl::ProcessKey(eKey
+ {
+ if (!Active())
+ return osEnd;
++#ifdef USE_JUMPPLAY
++ marks.Reload();
++#endif /* JUMPPLAY */
+ if (visible) {
+ if (timeoutShow && time(NULL) > timeoutShow) {
+ Hide();
+@@ -4413,7 +6237,32 @@ eOSState cReplayControl::ProcessKey(eKey
+ TimeSearchProcess(Key);
+ return osContinue;
+ }
++#ifdef USE_DVDARCHIVE
++ bool isOnMark = false;
++ if (canJumpChapters) {
++ int Current, Total;
++ GetIndex(Current, Total);
++ cMark *m = marks.Get(Current);
++ if (m && (m->position == Current)) isOnMark = true;
++ }
++#endif /* DVDARCHIVE */
+ bool DoShowMode = true;
++#ifdef USE_VOLCTRL
++ if (Setup.LRVolumeControl && (!Setup.LRForwardRewind || (Setup.LRForwardRewind == 1 && !visible))) {
++ switch (Key) {
++ // Left/Right volume control
++ case kLeft|k_Repeat:
++ case kLeft:
++ case kRight|k_Repeat:
++ case kRight:
++ cRemote::Put(NORMALKEY(Key) == kLeft ? kVolDn : kVolUp, true);
++ return osContinue;
++ break;
++ default:
++ break;
++ }
++ }
++#endif /* VOLCTRL */
+ switch (Key) {
+ // Positioning:
+ case kPlay:
+@@ -4431,25 +6280,82 @@ eOSState cReplayControl::ProcessKey(eKey
+ case kFastFwd:
+ case kRight: Forward(); break;
+ case kRed: TimeSearch(); break;
++#ifdef USE_LIEMIEXT
++ case kGreen|k_Repeat:
++ case kGreen: SkipSeconds(-Setup.JumpSeconds); break;
++ case kYellow|k_Repeat:
++ case kYellow: SkipSeconds( Setup.JumpSeconds); break;
++ case k1|k_Repeat:
++ case k1: SkipSeconds(-Setup.JumpSecondsSlow); break;
++ case k3|k_Repeat:
++ case k3: SkipSeconds( Setup.JumpSecondsSlow); break;
++ case kPrev|k_Repeat:
++ case kPrev: if (lastSkipTimeout.TimedOut()) {
++ lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
++ lastSkipKey = kPrev;
++ }
++ else if (RAWKEY(lastSkipKey) != kPrev && lastSkipSeconds > (2 * REPLAYCONTROLSKIPLIMIT)) {
++ lastSkipSeconds /= 2;
++ lastSkipKey = kNone;
++ }
++ lastSkipTimeout.Set(REPLAYCONTROLSKIPTIMEOUT);
++ SkipSeconds(-lastSkipSeconds); break;
++ case kNext|k_Repeat:
++ case kNext: if (lastSkipTimeout.TimedOut()) {
++ lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
++ lastSkipKey = kNext;
++ }
++ else if (RAWKEY(lastSkipKey) != kNext && lastSkipSeconds > (2 * REPLAYCONTROLSKIPLIMIT)) {
++ lastSkipSeconds /= 2;
++ lastSkipKey = kNone;
++ }
++ lastSkipTimeout.Set(REPLAYCONTROLSKIPTIMEOUT);
++ SkipSeconds(lastSkipSeconds); break;
++#else
+ case kGreen|k_Repeat:
+ case kGreen: SkipSeconds(-60); break;
+ case kYellow|k_Repeat:
+ case kYellow: SkipSeconds( 60); break;
++#endif /* LIEMIEXT */
+ case kStop:
+ case kBlue: Hide();
+ Stop();
+ return osEnd;
++#ifdef USE_DVDARCHIVE
++ case kDvdChapterJumpForward|k_Repeat:
++ case kDvdChapterJumpForward: if (canJumpChapters && !isOnMark) {
++ ChaptersJump(true);
++ }
++ else {
++ DoShowMode = false;
++ MarkMove(true);
++ }
++ break;
++ case kDvdChapterJumpBack|k_Repeat:
++ case kDvdChapterJumpBack: if (canJumpChapters && !isOnMark) {
++ ChaptersJump(false);
++ }
++ else {
++ DoShowMode = false;
++ MarkMove(false);
++ }
++ break;
++#endif /* DVDARCHIVE */
+ default: {
+ DoShowMode = false;
+ switch (Key) {
+ // Editing:
+ case kMarkToggle: MarkToggle(); break;
++#ifndef USE_LIEMIEXT
+ case kPrev|k_Repeat:
+ case kPrev:
++#endif /* LIEMIEXT */
+ case kMarkJumpBack|k_Repeat:
+ case kMarkJumpBack: MarkJump(false); break;
++#ifndef USE_LIEMIEXT
+ case kNext|k_Repeat:
+ case kNext:
++#endif /* LIEMIEXT */
+ case kMarkJumpForward|k_Repeat:
+ case kMarkJumpForward: MarkJump(true); break;
+ case kMarkMoveBack|k_Repeat:
+@@ -4469,7 +6375,16 @@ eOSState cReplayControl::ProcessKey(eKey
+ else
+ Show();
+ break;
++#ifdef USE_DELTIMESHIFTREC
++ case kBack: { cRecordControl* rc = cRecordControls::GetRecordControl(fileName);
++ if (rc && rc->InstantId())
++ return osEnd;
++ else
++ return osRecordings;
++ }
++#else
+ case kBack: return osRecordings;
++#endif /* DELTIMESHIFTREC */
+ default: return osUnknown;
+ }
+ }
+diff -ruNp vdr-1.6.0-2/menu.h vdr-1.6.0-2-extensions/menu.h
+--- vdr-1.6.0-2/menu.h 2008-02-10 17:01:53.000000000 +0100
++++ vdr-1.6.0-2-extensions/menu.h 2009-04-09 20:48:26.000000000 +0200
+@@ -18,6 +18,9 @@
+ #include "menuitems.h"
+ #include "recorder.h"
+ #include "skins.h"
++#ifdef USE_SETUP
++#include "submenu.h"
++#endif /* SETUP */
+
+ class cMenuText : public cOsdMenu {
+ private:
+@@ -29,12 +32,19 @@ public:
+ void SetText(const char *Text);
+ virtual void Display(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuText"; }
++#endif /* GRAPHTFT */
+ };
+
+ class cMenuEditTimer : public cOsdMenu {
+ private:
+ cTimer *timer;
+ cTimer data;
++#ifdef USE_LIEMIEXT
++ char name[MaxFileName];
++ char path[MaxFileName];
++#endif /* LIEMIEXT */
+ int channel;
+ bool addIfConfirmed;
+ cMenuEditDateItem *firstday;
+@@ -43,6 +53,9 @@ public:
+ cMenuEditTimer(cTimer *Timer, bool New = false);
+ virtual ~cMenuEditTimer();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuTimerEdit"; }
++#endif /* GRAPHTFT */
+ };
+
+ class cMenuEvent : public cOsdMenu {
+@@ -52,22 +65,37 @@ public:
+ cMenuEvent(const cEvent *Event, bool CanSwitch = false, bool Buttons = false);
+ virtual void Display(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuEvent"; }
++#endif /* GRAPHTFT */
+ };
+
+ class cMenuMain : public cOsdMenu {
+ private:
++#ifdef USE_SETUP
++ int nrDynamicMenuEntries;
++#endif /* SETUP */
+ bool replaying;
+ cOsdItem *stopReplayItem;
+ cOsdItem *cancelEditingItem;
+ cOsdItem *stopRecordingItem;
+ int recordControlsState;
+ static cOsdObject *pluginOsdObject;
++#ifdef USE_SETUP
++ void Set(int current=0);
++ bool Update(bool Force = false);
++ cSubMenu subMenu;
++#else
+ void Set(void);
+ bool Update(bool Force = false);
++#endif /* SETUP */
+ public:
+ cMenuMain(eOSState State = osUnknown);
+ virtual eOSState ProcessKey(eKeys Key);
+ static cOsdObject *PluginOsdObject(void);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuMain"; }
++#endif /* GRAPHTFT */
+ };
+
+ class cDisplayChannel : public cOsdObject {
+@@ -163,12 +191,18 @@ private:
+ eOSState Delete(void);
+ eOSState Info(void);
+ eOSState Commands(eKeys Key = kNone);
++#ifdef USE_LIEMIEXT
++ eOSState Rename(void);
++#endif /* LIEMIEXT */
+ protected:
+ cRecording *GetRecording(cMenuRecordingItem *Item);
+ public:
+ cMenuRecordings(const char *Base = NULL, int Level = 0, bool OpenSubMenus = false);
+ ~cMenuRecordings();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuRecordings"; }
++#endif /* GRAPHTFT */
+ };
+
+ class cRecordControl {
+@@ -212,11 +246,21 @@ public:
+ class cReplayControl : public cDvbPlayerControl {
+ private:
+ cSkinDisplayReplay *displayReplay;
++#ifdef USE_JUMPPLAY
++ cMarksReload marks;
++#else
+ cMarks marks;
++#endif /* JUMPPLAY */
+ bool visible, modeOnly, shown, displayFrames;
+ int lastCurrent, lastTotal;
+ bool lastPlay, lastForward;
+ int lastSpeed;
++#ifdef USE_LIEMIEXT
++ int lastSkipSeconds;
++ int lastSkipSecondsSlow;
++ eKeys lastSkipKey;
++ cTimeMs lastSkipTimeout;
++#endif /* LIEMIEXT */
+ time_t timeoutShow;
+ bool timeSearchActive, timeSearchHide;
+ int timeSearchTime, timeSearchPos;
+@@ -234,9 +278,17 @@ private:
+ void MarkMove(bool Forward);
+ void EditCut(void);
+ void EditTest(void);
++#ifdef USE_DVDARCHIVE
++ void ChaptersJump(bool Forward);
++ bool canJumpChapters;
++ char *dvdchapters;
++#endif /* DVDARCHIVE */
+ public:
+ cReplayControl(void);
+ virtual ~cReplayControl();
++#ifdef USE_DELTIMESHIFTREC
++ void Stop(void);
++#endif /* DELTIMESHIFTREC */
+ virtual cOsdObject *GetInfo(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ virtual void Show(void);
+diff -ruNp vdr-1.6.0-2/menuitems.c vdr-1.6.0-2-extensions/menuitems.c
+--- vdr-1.6.0-2/menuitems.c 2008-02-10 17:03:30.000000000 +0100
++++ vdr-1.6.0-2-extensions/menuitems.c 2009-04-09 20:48:26.000000000 +0200
+@@ -32,9 +32,20 @@ cMenuEditItem::~cMenuEditItem()
+ free(name);
+ }
+
++#ifdef USE_VALIDINPUT
++void cMenuEditItem::SetValue(const char *Value, bool HasPre, bool HasSucc)
++#else
+ void cMenuEditItem::SetValue(const char *Value)
++#endif /* VALIDINPUT */
+ {
+ cString buffer = cString::sprintf("%s:\t%s", name, Value);
++#ifdef USE_VALIDINPUT
++ if (Setup.ShowValidInput) {
++ if (HasPre && HasSucc) buffer = cString::sprintf("%s:\t<%s>", name, Value);
++ else if (HasPre) buffer = cString::sprintf("%s:\t<%s", name, Value);
++ else if (HasSucc) buffer = cString::sprintf("%s:\t%s>", name, Value);
++ }
++#endif /* VALIDINPUT */
+ SetText(buffer);
+ cStatus::MsgOsdCurrentItem(buffer);
+ }
+@@ -126,7 +137,11 @@ void cMenuEditBoolItem::Set(void)
+ {
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString);
++#ifdef USE_VALIDINPUT
++ SetValue(buf, *value, !*value);
++#else
+ SetValue(buf);
++#endif /* VALIDINPUT */
+ }
+
+ // --- cMenuEditBitItem ------------------------------------------------------
+@@ -619,6 +634,171 @@ eOSState cMenuEditStrItem::ProcessKey(eK
+ return osContinue;
+ }
+
++#ifdef USE_LIEMIEXT
++// --- cMenuEditRecPathItem --------------------------------------------------
++
++cMenuEditRecPathItem::cMenuEditRecPathItem(const char* Name, char* Path,
++ int Length): cMenuEditStrItem(Name, Path, Length, tr(FileNameChars))
++{
++ SetBase(Path);
++}
++
++cMenuEditRecPathItem::~cMenuEditRecPathItem()
++{
++}
++
++void cMenuEditRecPathItem::SetBase(const char* Path)
++{
++ if (!Path)
++ base[0] = 0;
++ Utf8Strn0Cpy(base, Path, sizeof(base));
++ char* p = strrchr(base, '~');
++ if (p)
++ p[0] = 0;
++ else
++ base[0] = 0;
++}
++
++void cMenuEditRecPathItem::FindNextLevel()
++{
++ char item[MaxFileName];
++
++ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
++ {
++ char* p;
++ Utf8Strn0Cpy(item, recording->Name(), sizeof(item));
++ stripspace(value);
++ lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++ if (!lengthUtf8)
++ p = strchr(item, '~');
++ else {
++ if (strstr(item, value) != item)
++ continue;
++ if (item[strlen(value)] != '~')
++ continue;
++ p = strchr(item + strlen(value) + 1, '~');
++ }
++ if (!p)
++ continue;
++ p[0] = 0;
++ Utf8Strn0Cpy(base, value, length);
++ Utf8Strn0Cpy(value, item, length);
++ lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++ return;
++ }
++}
++
++void cMenuEditRecPathItem::Find(bool Next)
++{
++ char item[MaxFileName];
++ char lastItem[MaxFileName] = "";
++
++ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
++ {
++ const char* recName = recording->Name();
++ if (Utf8StrLen(base) && strstr(recName, base) != recName)
++ continue;
++ if (strlen(base) && recName[strlen(base)] != '~')
++ continue;
++ Utf8Strn0Cpy(item, recName, sizeof(item));
++ char* p = strchr(item + strlen(base) + 1, '~');
++ if (!p)
++ continue;
++ p[0] = 0;
++ if (!Next && (strcmp(item, value) == 0)) {
++ if (strlen(lastItem))
++ Utf8Strn0Cpy(value, lastItem, length);
++ lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++ return;
++ }
++ if (strcmp(lastItem, item) != 0) {
++ if(Next && Utf8StrLen(lastItem) && strcmp(lastItem, value) == 0) {
++ Utf8Strn0Cpy(value, item, length);
++ lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++ return;
++ }
++ Utf8Strn0Cpy(lastItem, item, sizeof(lastItem));
++ }
++ }
++}
++
++void cMenuEditRecPathItem::SetHelpKeys(void)
++{
++ cSkinDisplay::Current()->SetButtons(tr("Rename$Up"), tr("Rename$Down"), tr("Rename$Previous"), tr("Rename$Next"));
++}
++
++eOSState cMenuEditRecPathItem::ProcessKey(eKeys Key)
++{
++ switch (Key) {
++ case kLeft:
++ case kRed: // one level up
++ if (!InEditMode())
++ return cMenuEditItem::ProcessKey(Key);
++ Utf8Strn0Cpy(value, base, lengthUtf8);
++ lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++ SetBase(base);
++ pos = Utf8StrLen(base);
++ if (pos)
++ pos++;
++ if (!lengthUtf8) {
++ Utf8Strn0Cpy(value, " ", length);
++ lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++ }
++ break;
++ case kRight:
++ case kGreen: // one level down
++ if (InEditMode())
++ FindNextLevel();
++ else
++ EnterEditMode();
++
++ if (!lengthUtf8) {
++ Utf8Strn0Cpy(value, " ", length);
++ lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++ }
++ pos = Utf8StrLen(base);
++ if (pos)
++ pos++;
++ SetHelpKeys();
++ break;
++ case kUp|k_Repeat:
++ case kUp:
++ case kYellow|k_Repeat:
++ case kYellow: // previous directory in list
++ if (!InEditMode())
++ return cMenuEditItem::ProcessKey(Key);
++ Find(false);
++ pos = Utf8StrLen(base);
++ if (pos)
++ pos++;
++ break;
++ case kDown|k_Repeat:
++ case kDown:
++ case kBlue|k_Repeat:
++ case kBlue: // next directory in list
++ if (!InEditMode())
++ return cMenuEditItem::ProcessKey(Key);
++ Find(true);
++ pos = Utf8StrLen(base);
++ if (pos)
++ pos++;
++ break;
++ case kOk: // done
++ if (!InEditMode())
++ return cMenuEditItem::ProcessKey(Key);
++ stripspace(value);
++ lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++ cSkinDisplay::Current()->SetButtons(NULL);
++ LeaveEditMode(Key == kOk);
++ break;
++ default:
++ return cMenuEditItem::ProcessKey(Key);
++ }
++ Set();
++ return osContinue;
++}
++#endif /* LIEMIEXT */
++
+ // --- cMenuEditStraItem -----------------------------------------------------
+
+ cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings)
+@@ -630,7 +810,11 @@ cMenuEditStraItem::cMenuEditStraItem(con
+
+ void cMenuEditStraItem::Set(void)
+ {
++#ifdef USE_VALIDINPUT
++ SetValue(strings[*value], (*value > min), (*value < max));
++#else
+ SetValue(strings[*value]);
++#endif /* VALIDINPUT */
+ }
+
+ // --- cMenuEditChanItem -----------------------------------------------------
+diff -ruNp vdr-1.6.0-2/menuitems.h vdr-1.6.0-2-extensions/menuitems.h
+--- vdr-1.6.0-2/menuitems.h 2008-02-16 17:09:58.000000000 +0100
++++ vdr-1.6.0-2-extensions/menuitems.h 2009-04-09 20:48:26.000000000 +0200
+@@ -21,7 +21,11 @@ private:
+ public:
+ cMenuEditItem(const char *Name);
+ ~cMenuEditItem();
++#ifdef USE_VALIDINPUT
++ void SetValue(const char *Value, bool HasPre=false, bool HasSucc=false);
++#else
+ void SetValue(const char *Value);
++#endif /* VALIDINPUT */
+ };
+
+ class cMenuEditIntItem : public cMenuEditItem {
+@@ -78,26 +82,46 @@ public:
+
+ class cMenuEditStrItem : public cMenuEditItem {
+ private:
++#ifdef USE_LIEMIEXT
++ int offset;
++#else
+ char *value;
+ int length;
+ const char *allowed;
+ int pos, offset;
++#endif /* LIEMIEXT */
+ bool insert, newchar, uppercase;
++#ifndef USE_LIEMIEXT
+ int lengthUtf8;
+ uint *valueUtf8;
++#endif /* LIEMIEXT */
+ uint *allowedUtf8;
+ uint *charMapUtf8;
+ uint *currentCharUtf8;
+ eKeys lastKey;
+ cTimeMs autoAdvanceTimeout;
++#ifndef USE_LIEMIEXT
+ void SetHelpKeys(void);
++#endif /* LIEMIEXT */
+ uint *IsAllowed(uint c);
+ void AdvancePos(void);
++#ifndef USE_LIEMIEXT
+ virtual void Set(void);
++#endif /* LIEMIEXT */
+ uint Inc(uint c, bool Up);
+ void Insert(void);
+ void Delete(void);
+ protected:
++#ifdef USE_LIEMIEXT
++ char *value;
++ int length;
++ uint *valueUtf8;
++ int lengthUtf8;
++ const char *allowed;
++ int pos;
++ void SetHelpKeys(void);
++ virtual void Set(void);
++#endif /* LIEMIEXT */
+ void EnterEditMode(void);
+ void LeaveEditMode(bool SaveValue = false);
+ bool InEditMode(void) { return valueUtf8 != NULL; }
+@@ -107,6 +131,21 @@ public:
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
++#ifdef USE_LIEMIEXT
++class cMenuEditRecPathItem : public cMenuEditStrItem {
++protected:
++ char base[MaxFileName];
++ virtual void SetHelpKeys(void);
++ void SetBase(const char* Path);
++ void FindNextLevel();
++ void Find(bool Next);
++public:
++ cMenuEditRecPathItem(const char* Name, char* Path, int Length);
++ ~cMenuEditRecPathItem();
++ virtual eOSState ProcessKey(eKeys Key);
++ };
++#endif /* LIEMIEXT */
++
+ class cMenuEditStraItem : public cMenuEditIntItem {
+ private:
+ const char * const *strings;
+@@ -174,6 +213,9 @@ public:
+ cMenuSetupPage(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ void SetPlugin(cPlugin *Plugin);
++#ifdef USE_GRAPHTFT
++ const char* MenuKind() { return "MenuSetupPage"; }
++#endif /* GRAPHTFT */
+ };
+
+ #endif //__MENUITEMS_H
+diff -ruNp vdr-1.6.0-2/menuorgpatch.h vdr-1.6.0-2-extensions/menuorgpatch.h
+--- vdr-1.6.0-2/menuorgpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/menuorgpatch.h 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,102 @@
++#ifdef USE_MENUORG
++/*
++ * vdr-menuorg - A plugin for the Linux Video Disk Recorder
++ * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net>
++ *
++ * 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 of the License, 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
++ * this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ * $Id$
++ *
++ */
++
++#ifndef __MENUORGPATCH_H
++#define __MENUORGPATCH_H
++
++#include "mainmenuitemsprovider.h"
++
++class MenuOrgPatch
++{
++ private:
++ static IMainMenuItemsProvider* _mainMenuItemsProvider;
++
++ private:
++ static IMainMenuItemsProvider* MainMenuItemsProvider()
++ {
++ if (!_mainMenuItemsProvider)
++ {
++ IMainMenuItemsProvider* mainMenuItemsProvider;
++
++ if (cPluginManager::CallFirstService(MENU_ITEMS_PROVIDER_SERVICE_ID, &mainMenuItemsProvider))
++ {
++ _mainMenuItemsProvider = mainMenuItemsProvider;
++ }
++ }
++ return _mainMenuItemsProvider;
++ }
++
++ public:
++ static bool IsCustomMenuAvailable()
++ {
++ return (MainMenuItemsProvider() != NULL) && (MainMenuItemsProvider()->IsCustomMenuAvailable());
++ }
++
++ static void EnterRootMenu()
++ {
++ if (MainMenuItemsProvider())
++ {
++ MainMenuItemsProvider()->EnterRootMenu();
++ }
++ }
++
++ static bool LeaveSubMenu()
++ {
++ if (MainMenuItemsProvider())
++ {
++ return MainMenuItemsProvider()->LeaveSubMenu();
++ }
++ return false;
++ }
++
++ static void EnterSubMenu(cOsdItem* item)
++ {
++ if (MainMenuItemsProvider())
++ {
++ MainMenuItemsProvider()->EnterSubMenu(item);
++ }
++ }
++
++ static MenuItemDefinitions* MainMenuItems()
++ {
++ if (MainMenuItemsProvider())
++ {
++ return MainMenuItemsProvider()->MainMenuItems();
++ }
++ return NULL;
++ }
++
++ static cOsdMenu* Execute(cOsdItem* item)
++ {
++ if (MainMenuItemsProvider())
++ {
++ return MainMenuItemsProvider()->Execute(item);
++ }
++ return NULL;
++ }
++};
++
++IMainMenuItemsProvider* MenuOrgPatch::_mainMenuItemsProvider = NULL;
++
++#endif //__MENUORGPATCH_H
++#endif /* MENUORG */
+diff -ruNp vdr-1.6.0-2/osdbase.c vdr-1.6.0-2-extensions/osdbase.c
+--- vdr-1.6.0-2/osdbase.c 2008-02-17 12:33:04.000000000 +0100
++++ vdr-1.6.0-2-extensions/osdbase.c 2009-04-09 20:48:26.000000000 +0200
+@@ -22,6 +22,9 @@ cOsdItem::cOsdItem(eOSState State)
+ state = State;
+ selectable = true;
+ fresh = true;
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++ subMenu = 0;
++#endif /* SETUP & PINPLUGIN */
+ }
+
+ cOsdItem::cOsdItem(const char *Text, eOSState State, bool Selectable)
+@@ -31,8 +34,23 @@ cOsdItem::cOsdItem(const char *Text, eOS
+ selectable = Selectable;
+ fresh = true;
+ SetText(Text);
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++ subMenu = 0;
++#endif /* SETUP & PINPLUGIN */
+ }
+
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++cOsdItem::cOsdItem(const char *Text, eOSState State, cSubMenuNode* SubMenu)
++{
++ text = NULL;
++ state = State;
++ selectable = true;
++ fresh = true;
++ SetText(Text);
++ subMenu = SubMenu;
++}
++#endif /* SETUP & PINPLUGIN */
++
+ cOsdItem::~cOsdItem()
+ {
+ free(text);
+@@ -77,6 +95,9 @@ cOsdMenu::cOsdMenu(const char *Title, in
+ {
+ isMenu = true;
+ digit = 0;
++#ifdef USE_LIEMIEXT
++ key_nr = -1;
++#endif /* LIEMIEXT */
+ hasHotkeys = false;
+ title = NULL;
+ SetTitle(Title);
+@@ -97,6 +118,9 @@ cOsdMenu::~cOsdMenu()
+ free(status);
+ displayMenu->Clear();
+ cStatus::MsgOsdClear();
++#ifdef USE_GRAPHTFT
++ cStatus::MsgOsdMenuDestroy();
++#endif /* GRAPHTFT */
+ if (!--displayMenuCount)
+ DELETENULL(displayMenu);
+ }
+@@ -119,7 +143,11 @@ const char *cOsdMenu::hk(const char *s)
+ digit = -1; // prevents automatic hotkeys - input already has them
+ if (digit >= 0) {
+ digit++;
++#ifdef USE_LIEMIEXT
++ buffer = cString::sprintf(" %2d%s %s", digit, (digit > 9) ? "" : " ", s);
++#else
+ buffer = cString::sprintf(" %c %s", (digit < 10) ? '0' + digit : ' ' , s);
++#endif /* LIEMIEXT */
+ s = buffer;
+ }
+ }
+@@ -199,9 +227,15 @@ void cOsdMenu::Display(void)
+ subMenu->Display();
+ return;
+ }
++#ifdef USE_OSDMAXITEMS
++ displayMenuItems = displayMenu->MaxItems();
++#endif /* OSDMAXITEMS */
+ displayMenu->SetMessage(mtStatus, NULL);
+ displayMenu->Clear();
+ cStatus::MsgOsdClear();
++#ifdef USE_GRAPHTFT
++ cStatus::MsgOsdMenuDisplay(MenuKind());
++#endif /* GRAPHTFT */
+ displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
+ displayMenu->SetTitle(title);
+ cStatus::MsgOsdTitle(title);
+@@ -296,6 +330,9 @@ bool cOsdMenu::SelectableItem(int idx)
+
+ void cOsdMenu::CursorUp(void)
+ {
++#ifdef USE_OSDMAXITEMS
++ displayMenuItems = displayMenu->MaxItems();
++#endif /* OSDMAXITEMS */
+ int tmpCurrent = current;
+ int lastOnScreen = first + displayMenuItems - 1;
+ int last = Count() - 1;
+@@ -334,6 +371,9 @@ void cOsdMenu::CursorUp(void)
+
+ void cOsdMenu::CursorDown(void)
+ {
++#ifdef USE_OSDMAXITEMS
++ displayMenuItems = displayMenu->MaxItems();
++#endif /* OSDMAXITEMS */
+ int tmpCurrent = current;
+ int lastOnScreen = first + displayMenuItems - 1;
+ int last = Count() - 1;
+@@ -374,6 +414,9 @@ void cOsdMenu::CursorDown(void)
+
+ void cOsdMenu::PageUp(void)
+ {
++#ifdef USE_OSDMAXITEMS
++ displayMenuItems = displayMenu->MaxItems();
++#endif /* OSDMAXITEMS */
+ int oldCurrent = current;
+ int oldFirst = first;
+ current -= displayMenuItems;
+@@ -408,6 +451,9 @@ void cOsdMenu::PageUp(void)
+
+ void cOsdMenu::PageDown(void)
+ {
++#ifdef USE_OSDMAXITEMS
++ displayMenuItems = displayMenu->MaxItems();
++#endif /* OSDMAXITEMS */
+ int oldCurrent = current;
+ int oldFirst = first;
+ current += displayMenuItems;
+@@ -448,6 +494,64 @@ void cOsdMenu::Mark(void)
+ }
+ }
+
++#ifdef USE_LIEMIEXT
++#define MENUKEY_TIMEOUT 1500
++
++eOSState cOsdMenu::HotKey(eKeys Key)
++{
++ bool match = false;
++ bool highlight = false;
++ int item_nr;
++ int i;
++
++ if (Key == kNone) {
++ if (lastActivity.TimedOut())
++ Key = kOk;
++ else
++ return osContinue;
++ }
++ else {
++ lastActivity.Set(MENUKEY_TIMEOUT);
++ }
++ for (cOsdItem *item = Last(); item; item = Prev(item)) {
++ const char *s = item->Text();
++ i = 0;
++ item_nr = 0;
++ if (s && (s = skipspace(s)) != '\0' && '0' <= s[i] && s[i] <= '9') {
++ do {
++ item_nr = item_nr * 10 + (s[i] - '0');
++ }
++ while (!((s[++i] == '\t')||(s[i] == ' ')) && (s[i] != '\0') && ('0' <= s[i]) && (s[i] <= '9'));
++ if ((Key == kOk) && (item_nr == key_nr)) {
++ current = item->Index();
++ RefreshCurrent();
++ Display();
++ cRemote::Put(kOk, true);
++ key_nr = -1;
++ break;
++ }
++ else if (Key != kOk) {
++ if (!highlight && (item_nr == (Key - k0))) {
++ highlight = true;
++ current = item->Index();
++ }
++ if (!match && (key_nr == -1) && ((item_nr / 10) == (Key - k0))) {
++ match = true;
++ key_nr = (Key - k0);
++ }
++ else if (((key_nr == -1) && (item_nr == (Key - k0))) || (!match && (key_nr >= 0) && (item_nr == (10 * key_nr + Key - k0)))) {
++ current = item->Index();
++ cRemote::Put(kOk, true);
++ key_nr = -1;
++ break;
++ }
++ }
++ }
++ }
++ if ((!match) && (Key != kNone)) {
++ key_nr = -1;
++ }
++#else
+ eOSState cOsdMenu::HotKey(eKeys Key)
+ {
+ for (cOsdItem *item = First(); item; item = Next(item)) {
+@@ -462,6 +566,7 @@ eOSState cOsdMenu::HotKey(eKeys Key)
+ }
+ }
+ }
++#endif /* LIEMIEXT */
+ return osContinue;
+ }
+
+@@ -500,8 +605,13 @@ eOSState cOsdMenu::ProcessKey(eKeys Key)
+ }
+ }
+ switch (Key) {
++#ifdef USE_LIEMIEXT
++ case kNone:
++ case k0...k9: return hasHotkeys ? HotKey(Key) : osUnknown;
++#else
+ case k0: return osUnknown;
+ case k1...k9: return hasHotkeys ? HotKey(Key) : osUnknown;
++#endif /* LIEMIEXT */
+ case kUp|k_Repeat:
+ case kUp: CursorUp(); break;
+ case kDown|k_Repeat:
+diff -ruNp vdr-1.6.0-2/osdbase.h vdr-1.6.0-2-extensions/osdbase.h
+--- vdr-1.6.0-2/osdbase.h 2007-11-03 15:50:52.000000000 +0100
++++ vdr-1.6.0-2-extensions/osdbase.h 2009-04-09 20:48:26.000000000 +0200
+@@ -15,6 +15,10 @@
+ #include "skins.h"
+ #include "tools.h"
+
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++#include "submenu.h"
++#endif /* SETUP & PINPLUGIN */
++
+ enum eOSState { osUnknown,
+ osContinue,
+ osSchedule,
+@@ -44,6 +48,9 @@ enum eOSState { osUnknown,
+ osUser8,
+ osUser9,
+ osUser10,
++#ifdef USE_LIVEBUFFER
++ osLiveBuffer,
++#endif /* LIVEBUFFER */
+ };
+
+ class cOsdItem : public cListObject {
+@@ -51,16 +58,26 @@ private:
+ char *text;
+ eOSState state;
+ bool selectable;
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++ cSubMenuNode* subMenu;
++#endif /* SETUP & PINPLUGIN */
+ protected:
+ bool fresh;
+ public:
+ cOsdItem(eOSState State = osUnknown);
+ cOsdItem(const char *Text, eOSState State = osUnknown, bool Selectable = true);
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++ cOsdItem(const char *Text, eOSState State, cSubMenuNode* SubMenu);
++#endif /* SETUP & PINPLUGIN */
+ virtual ~cOsdItem();
+ bool Selectable(void) const { return selectable; }
+ void SetText(const char *Text, bool Copy = true);
+ void SetSelectable(bool Selectable);
+ void SetFresh(bool Fresh);
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++ void SetSubMenu(cSubMenuNode* SubMenu) { subMenu = SubMenu; }
++ cSubMenuNode* SubMenu() { return subMenu; }
++#endif /* SETUP & PINPLUGIN */
+ const char *Text(void) const { return text; }
+ virtual void Set(void) {}
+ virtual eOSState ProcessKey(eKeys Key);
+@@ -95,6 +112,10 @@ private:
+ char *status;
+ int digit;
+ bool hasHotkeys;
++#ifdef USE_LIEMIEXT
++ int key_nr;
++ cTimeMs lastActivity;
++#endif /* LIEMIEXT */
+ protected:
+ void SetDisplayMenu(void);
+ cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; }
+@@ -129,6 +150,9 @@ public:
+ void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL);
+ virtual void Display(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuUnknown"; }
++#endif /* GRAPHTFT */
+ };
+
+ #endif //__OSDBASE_H
+diff -ruNp vdr-1.6.0-2/osd.c vdr-1.6.0-2-extensions/osd.c
+--- vdr-1.6.0-2/osd.c 2007-10-12 14:38:36.000000000 +0200
++++ vdr-1.6.0-2-extensions/osd.c 2009-04-09 20:48:26.000000000 +0200
+@@ -15,6 +15,9 @@
+ #include <sys/stat.h>
+ #include <sys/unistd.h>
+ #include "tools.h"
++#ifdef USE_TTXTSUBS
++#include "vdrttxtsubshooks.h"
++#endif /* TTXTSUBS */
+
+ // --- cPalette --------------------------------------------------------------
+
+@@ -720,6 +723,9 @@ int cOsd::osdTop = 0;
+ int cOsd::osdWidth = 0;
+ int cOsd::osdHeight = 0;
+ cVector<cOsd *> cOsd::Osds;
++#ifdef USE_PINPLUGIN
++bool cOsd::pinValid = false;
++#endif /* PINPLUGIN */
+
+ cOsd::cOsd(int Left, int Top, uint Level)
+ {
+@@ -730,6 +736,9 @@ cOsd::cOsd(int Left, int Top, uint Level
+ width = height = 0;
+ level = Level;
+ active = false;
++#ifdef USE_YAEPG
++ vidWin.bpp = 0;
++#endif /* YAEPG */
+ for (int i = 0; i < Osds.Size(); i++) {
+ if (Osds[i]->level > level) {
+ Osds.Insert(this, i);
+diff -ruNp vdr-1.6.0-2/osd.h vdr-1.6.0-2-extensions/osd.h
+--- vdr-1.6.0-2/osd.h 2007-10-12 16:28:44.000000000 +0200
++++ vdr-1.6.0-2-extensions/osd.h 2009-04-09 20:48:26.000000000 +0200
+@@ -400,6 +400,12 @@ public:
+ ///< 7: vertical, falling, upper
+ virtual void Flush(void);
+ ///< Actually commits all data to the OSD hardware.
++#ifdef USE_PINPLUGIN
++ static bool pinValid;
++#endif /* PINPLUGIN */
++#ifdef USE_YAEPG
++ tArea vidWin;
++#endif /* YAEPG */
+ };
+
+ class cOsdProvider {
+diff -ruNp vdr-1.6.0-2/player.c vdr-1.6.0-2-extensions/player.c
+--- vdr-1.6.0-2/player.c 2007-07-20 17:25:24.000000000 +0200
++++ vdr-1.6.0-2-extensions/player.c 2009-04-09 20:48:26.000000000 +0200
+@@ -60,10 +60,18 @@ cOsdObject *cControl::GetInfo(void)
+ return NULL;
+ }
+
++#ifdef USE_LIVEBUFFER
++cControl *cControl::Control(bool Hidden)
++#else
+ cControl *cControl::Control(void)
++#endif /* LIVEBUFFER */
+ {
+ cMutexLock MutexLock(&mutex);
++#ifdef USE_LIVEBUFFER
++ return (control && (!control->hidden || Hidden)) ? control : NULL;
++#else
+ return (control && !control->hidden) ? control : NULL;
++#endif /* LIVEBUFFER */
+ }
+
+ void cControl::Launch(cControl *Control)
+diff -ruNp vdr-1.6.0-2/player.h vdr-1.6.0-2-extensions/player.h
+--- vdr-1.6.0-2/player.h 2008-02-16 14:50:11.000000000 +0100
++++ vdr-1.6.0-2-extensions/player.h 2009-04-09 20:48:26.000000000 +0200
+@@ -83,7 +83,11 @@ public:
+ static void Launch(cControl *Control);
+ static void Attach(void);
+ static void Shutdown(void);
++#ifdef USE_LIVEBUFFER
++ static cControl *Control(bool Hidden = false);
++#else
+ static cControl *Control(void);
++#endif /* LIVEBUFFER */
+ };
+
+ #endif //__PLAYER_H
+diff -ruNp vdr-1.6.0-2/plugin.c vdr-1.6.0-2-extensions/plugin.c
+--- vdr-1.6.0-2/plugin.c 2008-02-17 14:32:12.000000000 +0100
++++ vdr-1.6.0-2-extensions/plugin.c 2009-04-09 20:48:26.000000000 +0200
+@@ -316,6 +316,14 @@ void cPluginManager::AddPlugin(const cha
+ char *p = strchr(s, ' ');
+ if (p)
+ *p = 0;
++#ifdef USE_PLUGINMISSING
++ struct stat st;
++ if (stat (cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), &st) && errno == ENOENT) {
++ esyslog("WARN: missing plugin '%s'", s);
++ fprintf(stderr, "vdr: missing plugin '%s'\n", s);
++ }
++ else
++#endif /* PLUGINMISSING */
+ dlls.Add(new cDll(cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), Args));
+ free(s);
+ }
+@@ -324,7 +332,11 @@ bool cPluginManager::LoadPlugins(bool Lo
+ {
+ for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
+ if (!dll->Load(Log))
++#ifdef USE_PLUGINMISSING
++ ;
++#else
+ return false;
++#endif /* PLUGINMISSING */
+ }
+ return true;
+ }
+diff -ruNp vdr-1.6.0-2/po/ca_ES.po vdr-1.6.0-2-extensions/po/ca_ES.po
+--- vdr-1.6.0-2/po/ca_ES.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/ca_ES.po 2009-04-09 20:48:26.000000000 +0200
+@@ -1001,3 +1001,270 @@ msgstr "Prem qualsevol tecla per cancel·
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR s'apagarà en %s minuts"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/cs_CZ.po vdr-1.6.0-2-extensions/po/cs_CZ.po
+--- vdr-1.6.0-2/po/cs_CZ.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/cs_CZ.po 2009-04-09 20:48:26.000000000 +0200
+@@ -999,3 +999,270 @@ msgstr "Jakákoliv klávesa zru¹í restart"
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR se vypne za %s minut"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/da_DK.po vdr-1.6.0-2-extensions/po/da_DK.po
+--- vdr-1.6.0-2/po/da_DK.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/da_DK.po 2009-04-09 20:48:26.000000000 +0200
+@@ -998,3 +998,270 @@ msgstr "Tryk vilkårlig knap for at annul
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR slukker om %s minutter"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/de_DE.po vdr-1.6.0-2-extensions/po/de_DE.po
+--- vdr-1.6.0-2/po/de_DE.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/de_DE.po 2009-04-09 20:48:26.000000000 +0200
+@@ -785,6 +785,30 @@ msgstr "Lautstärke beim Einschalten"
+ msgid "Setup.Miscellaneous$Emergency exit"
+ msgstr "Notausstieg"
+
++msgid "Setup.Miscellaneous$Volume ctrl with left/right"
++msgstr "Lautstärke mit Rechts/Links regeln"
++
++msgid "Setup.Miscellaneous$Channelgroups with left/right"
++msgstr "Kanalgruppen mit Rechts/Links"
++
++msgid "Setup.Miscellaneous$only in channelinfo"
++msgstr "nur in Kanalinfo"
++
++msgid "Setup.Miscellaneous$Search fwd/back with left/right"
++msgstr "Vor-/Rücklauf mit Rechts/Links"
++
++msgid "Setup.Miscellaneous$only in progress display"
++msgstr "nur in Fortschrittsanzeige"
++
++msgid "Setup.Miscellaneous$Lirc repeat delay"
++msgstr "Lirc Verzögerung"
++
++msgid "Setup.Miscellaneous$Lirc repeat freq"
++msgstr "Lirc Frequenz"
++
++msgid "Setup.Miscellaneous$Lirc repeat timeout"
++msgstr "Lirc Zeitbeschränkung"
++
+ msgid "Plugins"
+ msgstr "Plugins"
+
+@@ -998,3 +1022,327 @@ msgstr "Taste drücken, um Neustart abzub
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR wird in %s Minuten ausschalten"
++
++msgid "Parameters"
++msgstr "Parameter"
++
++msgid "Channel locked by LNB!"
++msgstr "Kanal durch LNB gesperrt!"
++
++msgid "Childlock"
++msgstr "Kindersicherung"
++
++msgid "Path"
++msgstr "Pfad"
++
++msgid "Timer commands"
++msgstr "Befehle für Aufzeichnungen"
++
++msgid "Rename recording"
++msgstr "Aufzeichnung umbenennen"
++
++msgid "Date"
++msgstr "Datum"
++
++msgid "Length"
++msgstr "Länge"
++
++msgid "Size"
++msgstr "Größe"
++
++msgid "Delete marks information?"
++msgstr "Marks löschen?"
++
++msgid "Delete resume information?"
++msgstr "Resume löschen?"
++
++msgid "DVD plugin is not installed!"
++msgstr "Das DVD-Plugin ist nicht installiert!"
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr "Alphabet für Haupt-, flexibel für Unterverzeichnisse"
++
++msgid "main dir by date, subdirs flexible"
++msgstr "Datum für Haupt-, flexibel für Unterverzeichnisse"
++
++msgid "all alphabetically"
++msgstr "Alles alphabetisch"
++
++msgid "all by date"
++msgstr "Alles nach Datum"
++
++msgid "Sorting"
++msgstr "Sortierung"
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr "WarEagle icons verwenden"
++
++msgid "Setup.OSD$Main menu title"
++msgstr "Hauptmenü Titel"
++
++msgid "Setup.OSD$- Text"
++msgstr "- Text"
++
++msgid "default"
++msgstr "Voreinstellung"
++
++msgid "VDR - text"
++msgstr "VDR - Text"
++
++msgid "text"
++msgstr "Text"
++
++msgid "VDR - version"
++msgstr "VDR - Version"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Befehle Position im Hauptmenü"
++
++msgid "Setup.OSD$Show valid input"
++msgstr "Zeige gültige Eingabe"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Zeitbalken anzeigen"
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "Zeitspanne für dop. EPG-Suche (min)"
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr "Doppelten externen EPG-Eintrag"
++
++msgid "Setup.EPG$Mode of noEPG-Patch"
++msgstr "Art des noEPG-Patches"
++
++msgid "adjust"
++msgstr "anwenden"
++
++msgid "delete"
++msgstr "löschen"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "Internes und externes EPG mischen"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "Erk. des lauf. VPS-Events abschalten"
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr "AC3-Transfer Fix benutzen"
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr "Sperre Kanäle"
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr "Kanal Sperren Filter"
++
++msgid "qam256"
++msgstr "qam256"
++
++msgid "dvb-c"
++msgstr "dvb-c"
++
++msgid "dvb-s"
++msgstr "dvb-s"
++
++msgid "all"
++msgstr "alle"
++
++msgid "blacklist"
++msgstr "Blacklist"
++
++msgid "whitelist"
++msgstr "Whitelist"
++
++msgid "has decoder"
++msgstr "mit Decoder"
++
++msgid "is primary"
++msgstr "ist primär"
++
++msgid "has decoder + is primary"
++msgstr "mit Decoder und primär"
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr "Sync Early Patch benutzen"
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr "DVB-Empfänger %d nutzt LNB Nr."
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr "LNB-Nutzung protokollieren"
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "Dolby Digital Ton aufzeichnen"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr "Videoverzeichnispolitik"
++
++msgid "Setup.Recording$Number of video directories"
++msgstr "Anzahl der Videoverzeichnisse"
++
++msgid "Setup.Recording$Video %d priority"
++msgstr "Video %d Priorität"
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr "Video %d min. MB frei"
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr "Freundliche Dateinamen"
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "Max. Aufnahmengröße (GB)"
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "Hard Link Cutter"
++
++msgid "Setup.Recording$Delete timeshift recording"
++msgstr "Zeitversetzte Aufnahme löschen"
++
++msgid "request"
++msgstr "abfragen"
++
++msgid "Setup.Recording$Show date"
++msgstr "Aufnahmedatum anzeigen"
++
++msgid "Setup.Recording$Show time"
++msgstr "Aufnahmezeit anzeigen"
++
++msgid "Setup.Recording$Show length"
++msgstr "Länge der Aufnahme anzeigen"
++
++msgid "Setup.Recording$Show end of timer"
++msgstr "Ende für Timer anzeigen"
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr "Aufnahmen sortieren nach"
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr "Verzeichnisse vor Aufnahmen einsortieren"
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr "Aufnahmen nach dem Schneiden löschen"
++
++msgid "Setup.Recording$Cutter adjust starttime"
++msgstr "Startzeit beim Schneiden anpassen"
++
++msgid "Setup.Replay$Jump&Play"
++msgstr "Wiedergabe nach Sprung"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Sprung bei Schnittmarke"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "Pause bei letzter Marke"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "Marken aktualisieren"
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr "Sprungweite in Sekunden"
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr "Sprungweite in Sekunden Langsam"
++
++msgid "Setup.Replay$Length"
++msgstr "Länge"
++
++msgid "Setup.Replay$Length / Number"
++msgstr "Länge / Nummer"
++
++msgid "Setup.Replay$Number"
++msgstr "Nummer"
++
++msgid "Setup.Replay$DVD display mode"
++msgstr "DVD Anzeige"
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr "DVD führende Nullen anzeigen"
++
++msgid "Setup.Replay$never"
++msgstr "nie"
++
++msgid "Setup.Replay$on begin"
++msgstr "am Anfang"
++
++msgid "Setup.Replay$on end"
++msgstr "am Ende"
++
++msgid "Setup.Replay$on begin and end"
++msgstr "am Anfang und Ende"
++
++msgid "Setup.Replay$Tray open"
++msgstr "DVD-Schublade öffnen"
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr "DVD drosseln auf"
++
++msgid "Jump"
++msgstr "Springen: "
++
++msgid "Skip +60s"
++msgstr "Sprung +60s"
++
++msgid "Skip -60s"
++msgstr "Sprung -60s"
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr "Schnitt bereits aktiv - zur Schnitt-Liste hinzufügen?"
++
++msgid "Rename$Up"
++msgstr "Höher"
++
++msgid "Rename$Down"
++msgstr "Tiefer"
++
++msgid "Rename$Previous"
++msgstr "Vorheriger"
++
++msgid "Rename$Next"
++msgstr "Nächster"
++
++msgid "Please mount %s"
++msgstr "Bitte %s einlegen!"
++
++msgid "Please mount DVD %04d"
++msgstr "Bitte DVD %04d einlegen!"
++
++msgid "Please mount DVD %d"
++msgstr "Bitte DVD %d einlegen!"
++
++msgid "Please wait. Checking DVD..."
++msgstr "Bitte warten. Überprüfe DVD..."
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr "Keine Index-Datei gefunden. Erstellung kann Minuten dauern. Erstellen?"
++
++msgid "Please wait. Creating index-file..."
++msgstr "Bitte warten. Index-Datei wird erstellt..."
++
++msgid "Wrong DVD!"
++msgstr "Falsche DVD!"
++
++msgid "Permanent Timeshift"
++msgstr "Permanentes Timeshift"
++
++msgid "Setup.LiveBuffer$Location"
++msgstr "Speicherort"
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr "Festplatte"
++
++msgid "Setup.LiveBuffer$memory"
++msgstr "Arbeitsspeicher"
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr "Puffergröße im Arbeitsspeicher (MB)"
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr "Auf Festplatte ausweiten wenn nötig"
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr "Pausierten Puffer beibehalten"
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr "Puffergröße auf Festplatte (MB)"
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr "Puffergröße (MB)"
+diff -ruNp vdr-1.6.0-2/po/el_GR.po vdr-1.6.0-2-extensions/po/el_GR.po
+--- vdr-1.6.0-2/po/el_GR.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/el_GR.po 2009-04-09 20:48:26.000000000 +0200
+@@ -998,3 +998,270 @@ msgstr ""
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr ""
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/es_ES.po vdr-1.6.0-2-extensions/po/es_ES.po
+--- vdr-1.6.0-2/po/es_ES.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/es_ES.po 2009-04-09 20:48:26.000000000 +0200
+@@ -999,3 +999,270 @@ msgstr "Pulse cualquier tecla para cance
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR se apagará en %s minutos"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/et_EE.po vdr-1.6.0-2-extensions/po/et_EE.po
+--- vdr-1.6.0-2/po/et_EE.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/et_EE.po 2009-04-09 20:48:26.000000000 +0200
+@@ -998,3 +998,270 @@ msgstr "Restardi katkestamiseks vajuta s
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR lülitub välja %s minuti pärast"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr "Ümbernimetamine"
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Käsu asukoht peamenüüs"
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Edenemisriba"
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr "Salvestuse kuupäev"
++
++msgid "Setup.Recording$Show time"
++msgstr "Salvestuse kellaaeg"
++
++msgid "Setup.Recording$Show length"
++msgstr "Salvestuse pikkus"
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr "Üles"
++
++msgid "Rename$Down"
++msgstr "Alla"
++
++msgid "Rename$Previous"
++msgstr "Eelmine"
++
++msgid "Rename$Next"
++msgstr "Järgmine"
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/fi_FI.po vdr-1.6.0-2-extensions/po/fi_FI.po
+--- vdr-1.6.0-2/po/fi_FI.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/fi_FI.po 2009-04-09 20:48:26.000000000 +0200
+@@ -30,6 +30,253 @@ msgstr "Siirtotilan aloitus epäonnistui!
+ msgid "Starting EPG scan"
+ msgstr "Ohjelmaoppaan päivitys aloitettu"
+
++msgid "Content$Movie/Drama"
++msgstr "Elokuva/draama"
++
++msgid "Content$Detective/Thriller"
++msgstr "Etsivä/trilleri"
++
++msgid "Content$Adventure/Western/War"
++msgstr "Seikkailu/western/sota"
++
++msgid "Content$Science Fiction/Fantasy/Horror"
++msgstr "Scifi/fantasia/kauhu"
++
++msgid "Content$Comedy"
++msgstr "Komedia"
++
++msgid "Content$Soap/Melodrama/Folkloric"
++msgstr "Saippua/melodraama/kansanperinne"
++
++msgid "Content$Romance"
++msgstr "Romanssi"
++
++msgid "Content$Serious/Classical/Religious/Historical Movie/Drama"
++msgstr "Vakava/klassinen/uskonnollinen/historiallinen elokuva/draama"
++
++msgid "Content$Adult Movie/Drama"
++msgstr "Aikuiselokuva/draama"
++
++msgid "Content$News/Current Affairs"
++msgstr "Uutiset/ajankohtaisohjelma"
++
++msgid "Content$News/Weather Report"
++msgstr "Uutiset/säätiedot"
++
++msgid "Content$News Magazine"
++msgstr "Uutismakasiini"
++
++msgid "Content$Documentary"
++msgstr "Dokumentti"
++
++msgid "Content$Discussion/Inverview/Debate"
++msgstr "Keskustelu/haastattelu/väittely"
++
++msgid "Content$Show/Game Show"
++msgstr "Show/visailu"
++
++msgid "Content$Game Show/Quiz/Contest"
++msgstr "Visailu/kilpailu"
++
++msgid "Content$Variety Show"
++msgstr "Varietee"
++
++msgid "Content$Talk Show"
++msgstr "Keskusteluohjelma"
++
++msgid "Content$Sports"
++msgstr "Urheilua"
++
++msgid "Content$Special Event"
++msgstr "Erikoistapahtuma"
++
++msgid "Content$Sport Magazine"
++msgstr "Urheilumakasiini"
++
++msgid "Content$Football"
++msgstr "Jalkapallo"
++
++msgid "Content$Tennis/Squash"
++msgstr "Tennis/Squash"
++
++msgid "Content$Team Sports"
++msgstr "Joukkueurheilua"
++
++msgid "Content$Athletics"
++msgstr "Yleisurheilua"
++
++msgid "Content$Motor Sport"
++msgstr "Moottoriurheilua"
++
++msgid "Content$Water Sport"
++msgstr "Vesiurheilua"
++
++msgid "Content$Winter Sports"
++msgstr "Talviurheilua"
++
++msgid "Content$Equestrian"
++msgstr "Ratsastusta"
++
++msgid "Content$Martial Sports"
++msgstr "Kamppailu-urheilua"
++
++msgid "Content$Children's/Youth Programmes"
++msgstr "Lasten ja nuorten ohjelma"
++
++msgid "Content$Pre-school Children's Programmes"
++msgstr "Alle kouluikäisten ohjelma"
++
++msgid "Content$Entertainment Programmes for 6 to 14"
++msgstr "Viihdeohjelma 6-14 vuotiaille"
++
++msgid "Content$Entertainment Programmes for 10 to 16"
++msgstr "Viihdeohjelma 10-16 vuotiaille"
++
++msgid "Content$Informational/Educational/School Programme"
++msgstr "Opetus/kouluohjelma"
++
++msgid "Content$Cartoons/Puppets"
++msgstr "Piirretty/nukke-esitys"
++
++msgid "Content$Music/Ballet/Dance"
++msgstr "Musiikki/baletti/tanssi"
++
++msgid "Content$Rock/Pop"
++msgstr "Rock/pop"
++
++msgid "Content$Serious/Classical Music"
++msgstr "Vakava/klassinen musiikki"
++
++msgid "Content$Folk/Tradional Music"
++msgstr "Folk/kansanmusiikki"
++
++msgid "Content$Jazz"
++msgstr "Jazz"
++
++msgid "Content$Musical/Opera"
++msgstr "Musikaali/ooppera"
++
++msgid "Content$Ballet"
++msgstr "Baletti"
++
++msgid "Content$Arts/Culture"
++msgstr "Taide/kulttuuri"
++
++msgid "Content$Performing Arts"
++msgstr "Performanssitaide"
++
++msgid "Content$Fine Arts"
++msgstr "Kuvataide"
++
++msgid "Content$Religion"
++msgstr "Uskonto"
++
++msgid "Content$Popular Culture/Traditional Arts"
++msgstr "Populaarikulttuuri/perinnetaiteet"
++
++msgid "Content$Literature"
++msgstr "Kirjallisuus"
++
++msgid "Content$Film/Cinema"
++msgstr "Elokuvataide"
++
++msgid "Content$Experimental Film/Video"
++msgstr "Kokeellinen elokuva/video"
++
++msgid "Content$Broadcasting/Press"
++msgstr "Televisio/radio/lehdistö"
++
++msgid "Content$New Media"
++msgstr "Uusmedia"
++
++msgid "Content$Arts/Culture Magazines"
++msgstr "Taide/kulttuurimakasiini"
++
++msgid "Content$Fashion"
++msgstr "Muoti"
++
++msgid "Content$Social/Political/Economics"
++msgstr "Yhteiskunta/politiikka/talous"
++
++msgid "Content$Magazines/Reports/Documentary"
++msgstr "Makasiini/reportaasi/dokumentti"
++
++msgid "Content$Economics/Social Advisory"
++msgstr "Talous/yhteiskunnallinen neuvonta"
++
++msgid "Content$Remarkable People"
++msgstr "Merkittävät henkilöt"
++
++msgid "Content$Education/Science/Factual"
++msgstr "Koulutus/tiede"
++
++msgid "Content$Nature/Animals/Environment"
++msgstr "Luonto/eläimet/ympäristö"
++
++msgid "Content$Technology/Natural Sciences"
++msgstr "Teknologia/luonnontiede"
++
++msgid "Content$Medicine/Physiology/Psychology"
++msgstr "Lääketiede/fysiologia/psykologia"
++
++msgid "Content$Foreign Countries/Expeditions"
++msgstr "Vieraat maat/tutkimusretket"
++
++msgid "Content$Social/Spiritual Sciences"
++msgstr "Yhteiskunta/hengelliset tieteet"
++
++msgid "Content$Further Education"
++msgstr "Jatkokoulutus"
++
++msgid "Content$Languages"
++msgstr "Kielet"
++
++msgid "Content$Leisure/Hobbies"
++msgstr "Vapaa-aika ja harrastukset"
++
++msgid "Content$Tourism/Travel"
++msgstr "Turismi/matkustaminen"
++
++msgid "Content$Handicraft"
++msgstr "Käsityöt"
++
++msgid "Content$Motoring"
++msgstr "Autoilu"
++
++msgid "Content$Fitness & Health"
++msgstr "Kuntoilu & terveys"
++
++msgid "Content$Cooking"
++msgstr "Ruuanlaitto"
++
++msgid "Content$Advertisement/Shopping"
++msgstr "Mainostaminen/ostaminen"
++
++msgid "Content$Gardening"
++msgstr "Puutarhanhoito"
++
++msgid "Content$Original Language"
++msgstr "Alkuperäiskieli"
++
++msgid "Content$Black & White"
++msgstr "Mustavalkoinen"
++
++msgid "Content$Unpublished"
++msgstr "Julkaisematon"
++
++msgid "Content$Live Broadcast"
++msgstr "Suoralähetys"
++
++msgid "Content$Special Characteristics"
++msgstr "Erikoisominaisuus"
++
++msgid "Content$Drama"
++msgstr "Draama"
++
++#, c-format
++msgid "Suitable for those aged %d and over"
++msgstr "Kielletty alle %d vuotiailta"
++
+ msgid "No title"
+ msgstr "Ei esitystä"
+
+@@ -1001,3 +1248,273 @@ msgstr "Peru uudelleenkäynnistys painama
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR sammuu %s minuutin kuluttua"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr "Polku"
++
++msgid "Timer commands"
++msgstr "Ajastinkomennot"
++
++msgid "Rename recording"
++msgstr "Nimeä tallenne"
++
++msgid "Date"
++msgstr "Päiväys"
++
++msgid "Length"
++msgstr "Pituus"
++
++msgid "Size"
++msgstr "Koko"
++
++msgid "Delete marks information?"
++msgstr "Poista tallenteen merkinnät"
++
++msgid "Delete resume information?"
++msgstr "Poista tallenteen paluutiedot"
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Komentojen sijainti päävalikossa"
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Näytä aikajana"
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "Dolby Digital tallennus"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr "Näytä tallenteen päiväys"
++
++msgid "Setup.Recording$Show time"
++msgstr "Näytä tallenteen ajankohta"
++
++msgid "Setup.Recording$Show length"
++msgstr "Näytä tallenteen kesto"
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr "Ylemmäs"
++
++msgid "Rename$Down"
++msgstr "Alemmas"
++
++msgid "Rename$Previous"
++msgstr "Edellinen"
++
++msgid "Rename$Next"
++msgstr "Seuraava"
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Parameters"
++msgstr "Parametrit"
++
++msgid "Permanent Timeshift"
++msgstr "Jatkuva ajansiirto"
++
++msgid "Setup.LiveBuffer$Location"
++msgstr "Puskurin sijainti"
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr "kovalevyllä"
++
++msgid "Setup.LiveBuffer$memory"
++msgstr "muistissa"
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr "Puffergröße im Arbeitsspeicher (MB)"
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr "Käytä kovalevyä tarvittaessa"
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr "Pidä pysäytetty puskuri"
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr "Puffergröße auf Festplatte (MB)"
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr "Puskurin koko (MB)"
+diff -ruNp vdr-1.6.0-2/po/fr_FR.po vdr-1.6.0-2-extensions/po/fr_FR.po
+--- vdr-1.6.0-2/po/fr_FR.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/fr_FR.po 2009-04-09 20:48:26.000000000 +0200
+@@ -8,6 +8,7 @@
+ # Pierre Briec <pbriec@free.fr>, 2006
+ # Bruno Roussel <bruno.roussel@free.fr>, 2007
+ # Michael Nival <mnival@club-internet.fr>, 2007
++# Patrice Staudt <patrice.staudt@laposte.net>, 2007, 2008
+ #
+ msgid ""
+ msgstr ""
+@@ -1004,3 +1005,270 @@ msgstr "Appuyer sur une touche pour annu
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR s'arrêtera dans %s minutes"
++
++msgid "Channel locked by LNB!"
++msgstr "Chaîne interdite par la LNB"
++
++msgid "Childlock"
++msgstr "Adulte"
++
++msgid "Path"
++msgstr "Dossiers"
++
++msgid "Timer commands"
++msgstr "Commandes de programmation"
++
++msgid "Rename recording"
++msgstr "Renommer l'enregistrement"
++
++msgid "Date"
++msgstr "Date"
++
++msgid "Length"
++msgstr "Longeur"
++
++msgid "Size"
++msgstr "Taille"
++
++msgid "Delete marks information?"
++msgstr "Effacer marque d'information?"
++
++msgid "Delete resume information?"
++msgstr "Effacer resume information?"
++
++msgid "DVD plugin is not installed!"
++msgstr "Le plugin de DVD n'est pas installé!"
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr "Dir principal alphabétiquement, subdirs flexibles"
++
++msgid "main dir by date, subdirs flexible"
++msgstr "Dir principal à la date, subdirs flexibles"
++
++msgid "all alphabetically"
++msgstr "tous alphabétiquement"
++
++msgid "all by date"
++msgstr "tous à la date"
++
++msgid "Sorting"
++msgstr "Triage"
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr "Icônes WarEagle"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Position des commandes dans le menu"
++
++msgid "Setup.OSD$Show valid input"
++msgstr "Afficher l'entrée valide"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Montrer la barre de progression"
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "Intervalle de recherche EPG de double(min)"
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr "Double entrée EPG externe"
++
++msgid "adjust"
++msgstr "ajuster"
++
++msgid "delete"
++msgstr "effacer"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "Mélanger les EPG interne et externe"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "Arreter la reconnaissance des événements VPS"
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr "Utiliser le correctif AC3-Transfer"
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr "Utilisez Sync début patch"
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr "La carte DVB %d utilise la LNB No."
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr "Protocoller l'utilisation du LNB"
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "Enregistrer en Dolby Digital"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr "Régles de répertoires vidéo"
++
++msgid "Setup.Recording$Number of video directories"
++msgstr "Nombre de répertoires vidéo"
++
++msgid "Setup.Recording$Video %d priority"
++msgstr "Video %d prioritaires"
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr "Video %d min. librei Mo"
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr "Noms de fichiers facile"
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "Taille max. de l'enregistrement (GB)"
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "Découpe Hard links"
++
++msgid "Setup.Recording$Show date"
++msgstr "Montrer la date d'enregistrement"
++
++msgid "Setup.Recording$Show time"
++msgstr "Montrer l'heure d'enregistrement"
++
++msgid "Setup.Recording$Show length"
++msgstr "Monter la longueur de l'enregistrement"
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr "Montrer la fin du temporisateur"
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr "Ordonner les enregistrement suivant"
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr "Les dossiers devant les enregistrements"
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr "Effacer l'enregistrement après la découpe"
++
++msgid "Setup.Replay$Jump&Play"
++msgstr "Lecture après saut"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Saut sur les marques de découpes"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "Pause après la dernière marque"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "Actualiser les marques"
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr "Longueur de saut en secondes"
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr "Longueur de saut lent en secondes"
++
++msgid "Setup.Replay$Length"
++msgstr "Longueur"
++
++msgid "Setup.Replay$Length / Number"
++msgstr "Longueur / Numéro"
++
++msgid "Setup.Replay$Number"
++msgstr "Numéro"
++
++msgid "Setup.Replay$DVD display mode"
++msgstr "Afficher le DVD"
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr "Afficher les zéros devant le numéro du DVD"
++
++msgid "Setup.Replay$never"
++msgstr "jamais"
++
++msgid "Setup.Replay$on begin"
++msgstr "au début"
++
++msgid "Setup.Replay$on end"
++msgstr "à la fin"
++
++msgid "Setup.Replay$on begin and end"
++msgstr "au début et à la fin"
++
++msgid "Setup.Replay$Tray open"
++msgstr "Ouvrir le tiroir du lecteur DVD"
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr "Ralentir la vitesse du DVD à"
++
++msgid "Jump"
++msgstr "Saut"
++
++msgid "Skip +60s"
++msgstr "Avance +60s"
++
++msgid "Skip -60s"
++msgstr "Recule -60s"
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr "Découpe déjà active - l'ajouter à la liste de découpe?"
++
++msgid "Rename$Up"
++msgstr "Haut"
++
++msgid "Rename$Down"
++msgstr "Bas"
++
++msgid "Rename$Previous"
++msgstr "Précédent"
++
++msgid "Rename$Next"
++msgstr "Suivant"
++
++msgid "Please mount %s"
++msgstr "Mettez %s dans le lecteur"
++
++msgid "Please mount DVD %04d"
++msgstr "Mettez le DVD %04d dans le lecteur"
++
++msgid "Please mount DVD %d"
++msgstr "Mettez le DVD %d dans le lecteur"
++
++msgid "Please wait. Checking DVD..."
++msgstr "Un moment SVP. Vérification du DVD..."
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr "Pas trouvé de fichiers index. La création de ce ficher prends quelques minutes. Créer?"
++
++msgid "Please wait. Creating index-file..."
++msgstr "Attendez, svp. Le fichier index est en cours de création..."
++
++msgid "Wrong DVD!"
++msgstr "Mauvais DVD!"
++
++msgid "Permanent Timeshift"
++msgstr "Timeshift permanent"
++
++msgid "Setup.LiveBuffer$Location"
++msgstr "Localisation"
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr "disque dur"
++
++msgid "Setup.LiveBuffer$memory"
++msgstr "mémoire"
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr "Taille de la mémoire tampon dans la mémoire (Mo)"
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr "Ètendre au disque dur, si nécessaire"
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr "Gardez en veille la mémoire tampon"
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr "Taille de la mémoire tampon sur le disque dur (Mo)"
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr "Taille de la mémoire tampon (Mo)"
+diff -ruNp vdr-1.6.0-2/po/hr_HR.po vdr-1.6.0-2-extensions/po/hr_HR.po
+--- vdr-1.6.0-2/po/hr_HR.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/hr_HR.po 2009-04-09 20:48:26.000000000 +0200
+@@ -1000,3 +1000,270 @@ msgstr "Pritisnite jednu tipku za poni¹t
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR æe se iskljuèiti za %s minuta"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/hu_HU.po vdr-1.6.0-2-extensions/po/hu_HU.po
+--- vdr-1.6.0-2/po/hu_HU.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/hu_HU.po 2009-04-09 20:48:26.000000000 +0200
+@@ -1002,3 +1002,270 @@ msgstr "Nyomj egy gombot az újraindítás
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "A VDR leáll %s perc múlva"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/it_IT.po vdr-1.6.0-2-extensions/po/it_IT.po
+--- vdr-1.6.0-2/po/it_IT.po 2008-09-11 13:29:43.000000000 +0200
++++ vdr-1.6.0-2-extensions/po/it_IT.po 2009-04-09 20:48:26.000000000 +0200
+@@ -11,8 +11,8 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-02-10 12:22+0100\n"
+-"PO-Revision-Date: 2008-04-17 01:07+0100\n"
++"POT-Creation-Date: 2008-10-05 05:55+0200\n"
++"PO-Revision-Date: 2008-10-05 05:57+0100\n"
+ "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
+ "Language-Team: Italian\n"
+ "MIME-Version: 1.0\n"
+@@ -22,6 +22,9 @@ msgstr ""
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Canale NON valido ***"
+
++msgid "Channel locked by LNB!"
++msgstr "Canale bloccato dal LNB!"
++
+ msgid "Channel not available!"
+ msgstr "Canale non disponibile!"
+
+@@ -31,6 +34,253 @@ msgstr "Impossibile avviare mod. trasfer
+ msgid "Starting EPG scan"
+ msgstr "Inizio scansione EPG"
+
++msgid "Content$Movie/Drama"
++msgstr "Film/Dramma"
++
++msgid "Content$Detective/Thriller"
++msgstr "Investigativo/Giallo"
++
++msgid "Content$Adventure/Western/War"
++msgstr "Avventura/Western/Guerra"
++
++msgid "Content$Science Fiction/Fantasy/Horror"
++msgstr "Finzione/Fantasia/Horror"
++
++msgid "Content$Comedy"
++msgstr "Commedia"
++
++msgid "Content$Soap/Melodrama/Folkloric"
++msgstr "Telenovella/Melodramma/Folcloristico"
++
++msgid "Content$Romance"
++msgstr "Romanzo"
++
++msgid "Content$Serious/Classical/Religious/Historical Movie/Drama"
++msgstr "Serio/Classico/Religioso/Film storico/Dramma"
++
++msgid "Content$Adult Movie/Drama"
++msgstr "Film per adulti/Dramma"
++
++msgid "Content$News/Current Affairs"
++msgstr "Notizie/Ultima ora"
++
++msgid "Content$News/Weather Report"
++msgstr "Notizie/Previsioni meteo"
++
++msgid "Content$News Magazine"
++msgstr "Rivista di notizie"
++
++msgid "Content$Documentary"
++msgstr "Documentario"
++
++msgid "Content$Discussion/Inverview/Debate"
++msgstr "Discussione/Intervista/Dibattito"
++
++msgid "Content$Show/Game Show"
++msgstr "Spettacolo/Gioco a premi"
++
++msgid "Content$Game Show/Quiz/Contest"
++msgstr "Gioco a premi/Quiz/Gara"
++
++msgid "Content$Variety Show"
++msgstr "Spettacolo di varietà"
++
++msgid "Content$Talk Show"
++msgstr "Talk Show"
++
++msgid "Content$Sports"
++msgstr "Sport"
++
++msgid "Content$Special Event"
++msgstr "Evento speciale"
++
++msgid "Content$Sport Magazine"
++msgstr "Rivista di sport"
++
++msgid "Content$Football"
++msgstr "Calcio"
++
++msgid "Content$Tennis/Squash"
++msgstr "Tennis/Squash"
++
++msgid "Content$Team Sports"
++msgstr "Sport di squadra"
++
++msgid "Content$Athletics"
++msgstr "Atletica"
++
++msgid "Content$Motor Sport"
++msgstr "Sport motoristici"
++
++msgid "Content$Water Sport"
++msgstr "Sport acquatici"
++
++msgid "Content$Winter Sports"
++msgstr "Sport invernali"
++
++msgid "Content$Equestrian"
++msgstr "Equitazione"
++
++msgid "Content$Martial Sports"
++msgstr "Arti marziali"
++
++msgid "Content$Children's/Youth Programmes"
++msgstr "Programmi per ragazzi/giovani"
++
++msgid "Content$Pre-school Children's Programmes"
++msgstr "Programmi per ragazzi prescolastici"
++
++msgid "Content$Entertainment Programmes for 6 to 14"
++msgstr "Programmi di intrattenimento da 6 a 14"
++
++msgid "Content$Entertainment Programmes for 10 to 16"
++msgstr "Programmi di intrattenimento da 10 a 16"
++
++msgid "Content$Informational/Educational/School Programme"
++msgstr "Informativo/Educativo/Programma scolastico"
++
++msgid "Content$Cartoons/Puppets"
++msgstr "Cartoni/Pupazzi"
++
++msgid "Content$Music/Ballet/Dance"
++msgstr "Musica/Balletto/Danza"
++
++msgid "Content$Rock/Pop"
++msgstr "Rock/Pop"
++
++msgid "Content$Serious/Classical Music"
++msgstr "Serio/Musica classica"
++
++msgid "Content$Folk/Tradional Music"
++msgstr "Folclore/Musica tradizionale"
++
++msgid "Content$Jazz"
++msgstr "Jazz"
++
++msgid "Content$Musical/Opera"
++msgstr "Musical/Opera"
++
++msgid "Content$Ballet"
++msgstr "Balletto"
++
++msgid "Content$Arts/Culture"
++msgstr "Arte/Cultura"
++
++msgid "Content$Performing Arts"
++msgstr "Arti di rendimento"
++
++msgid "Content$Fine Arts"
++msgstr "Arti fine"
++
++msgid "Content$Religion"
++msgstr "Religione"
++
++msgid "Content$Popular Culture/Traditional Arts"
++msgstr "Cultura popolare/Arti tradizionali"
++
++msgid "Content$Literature"
++msgstr "Letteratura"
++
++msgid "Content$Film/Cinema"
++msgstr "Film/Cinema"
++
++msgid "Content$Experimental Film/Video"
++msgstr "Film esperimentale/Video"
++
++msgid "Content$Broadcasting/Press"
++msgstr "Trasmissione/Stampa"
++
++msgid "Content$New Media"
++msgstr "Nuovo programma"
++
++msgid "Content$Arts/Culture Magazines"
++msgstr "Arte/Riviste di cultura"
++
++msgid "Content$Fashion"
++msgstr "Moda"
++
++msgid "Content$Social/Political/Economics"
++msgstr "Società/Politica/Economia"
++
++msgid "Content$Magazines/Reports/Documentary"
++msgstr "Riviste/Reportage/Documentari"
++
++msgid "Content$Economics/Social Advisory"
++msgstr "Economia/Consulenza sociale"
++
++msgid "Content$Remarkable People"
++msgstr "Personaggi importanti"
++
++msgid "Content$Education/Science/Factual"
++msgstr "Educazione/Scienza/Fatti"
++
++msgid "Content$Nature/Animals/Environment"
++msgstr "Natura/Animali/Ambiente"
++
++msgid "Content$Technology/Natural Sciences"
++msgstr "Tecnologia/Scienze naturali"
++
++msgid "Content$Medicine/Physiology/Psychology"
++msgstr "Medicina/Filosofia/Psicologia"
++
++msgid "Content$Foreign Countries/Expeditions"
++msgstr "Paesi esteri/Spedizioni"
++
++msgid "Content$Social/Spiritual Sciences"
++msgstr "Società/Scienze spirituali"
++
++msgid "Content$Further Education"
++msgstr "Altra educazione"
++
++msgid "Content$Languages"
++msgstr "Lingua"
++
++msgid "Content$Leisure/Hobbies"
++msgstr "Tempo libero/Hobby"
++
++msgid "Content$Tourism/Travel"
++msgstr "Turismo/Viaggi"
++
++msgid "Content$Handicraft"
++msgstr "Artigianato"
++
++msgid "Content$Motoring"
++msgstr "Motori"
++
++msgid "Content$Fitness & Health"
++msgstr "Culturismo & Salute"
++
++msgid "Content$Cooking"
++msgstr "Cucina"
++
++msgid "Content$Advertisement/Shopping"
++msgstr "Pubblicità/Acquisti"
++
++msgid "Content$Gardening"
++msgstr "Giardinaggio"
++
++msgid "Content$Original Language"
++msgstr "Lingua madre"
++
++msgid "Content$Black & White"
++msgstr "Bianco & Nero"
++
++msgid "Content$Unpublished"
++msgstr "Non pubblicato"
++
++msgid "Content$Live Broadcast"
++msgstr "ys"
++
++msgid "Content$Special Characteristics"
++msgstr "Caratteristiche speciali"
++
++msgid "Content$Drama"
++msgstr "Dramma"
++
++#, c-format
++msgid "Suitable for those aged %d and over"
++msgstr "Adatto per coloro con %d di età e oltre"
++
+ msgid "No title"
+ msgstr "Senza titolo"
+
+@@ -217,6 +467,9 @@ msgstr "Utente8"
+ msgid "Key$User9"
+ msgstr "Utente9"
+
++msgid "Recording started"
++msgstr "Registrazione avviata"
++
+ msgid "Disk"
+ msgstr "Disco"
+
+@@ -313,6 +566,9 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Gerarchia"
+
++msgid "Parameters"
++msgstr "Parametri"
++
+ msgid "Channel settings are not unique!"
+ msgstr "Parametri canale non univoci!"
+
+@@ -364,9 +620,21 @@ msgstr "Priorità"
+ msgid "Lifetime"
+ msgstr "Durata"
+
++msgid "Childlock"
++msgstr "Filtro fam."
++
++msgid "yes"
++msgstr "sì"
++
++msgid "no"
++msgstr "no"
++
+ msgid "File"
+ msgstr "Nome"
+
++msgid "Path"
++msgstr "Percorso"
++
+ msgid "First day"
+ msgstr "1° giorno"
+
+@@ -385,6 +653,9 @@ msgstr "Eliminare il timer?"
+ msgid "Timer still recording - really delete?"
+ msgstr "Timer in registrazione - eliminare?"
+
++msgid "Timer commands"
++msgstr "Comandi timer"
++
+ msgid "Event"
+ msgstr "Evento"
+
+@@ -445,6 +716,27 @@ msgstr "Riproduci"
+ msgid "Button$Rewind"
+ msgstr "Riavvolgi"
+
++msgid "Rename recording"
++msgstr "Rinomina registrazione"
++
++msgid "Date"
++msgstr "Data"
++
++msgid "Length"
++msgstr "Durata"
++
++msgid "Size"
++msgstr "Dimensione"
++
++msgid "Delete marks information?"
++msgstr "Eliminare informazione marcatori?"
++
++msgid "Delete resume information?"
++msgstr "Eliminare informazione ripristino?"
++
++msgid "Error while accessing recording!"
++msgstr "Errore accesso alla registrazione!"
++
+ msgid "Recordings"
+ msgstr "Registrazioni"
+
+@@ -454,8 +746,8 @@ msgstr "Apri"
+ msgid "Commands"
+ msgstr "Comandi"
+
+-msgid "Error while accessing recording!"
+-msgstr "Errore accesso alla registrazione!"
++msgid "DVD plugin is not installed!"
++msgstr "Il plugin DVD non è installato!"
+
+ msgid "Delete recording?"
+ msgstr "Eliminare la registrazione?"
+@@ -466,6 +758,21 @@ msgstr "Errore eliminazione registrazion
+ msgid "Recording commands"
+ msgstr "Comandi di registrazione"
+
++msgid "main dir alphabetically, subdirs flexible"
++msgstr "Dir. princ. in ordine alfabetico, sottodir. flessibili"
++
++msgid "main dir by date, subdirs flexible"
++msgstr "Dir. princ. per data, sottodir. flessibili"
++
++msgid "all alphabetically"
++msgstr "Tutte in ordine alfabetico"
++
++msgid "all by date"
++msgstr "Tutte per data"
++
++msgid "Sorting"
++msgstr "Ordinamento"
++
+ msgid "never"
+ msgstr "mai"
+
+@@ -475,6 +782,18 @@ msgstr "in base allo stile interfaccia"
+ msgid "always"
+ msgstr "sempre"
+
++msgid "default"
++msgstr "predefinito"
++
++msgid "VDR - text"
++msgstr "VDR - Testo"
++
++msgid "text"
++msgstr "Testo"
++
++msgid "VDR - version"
++msgstr "VDR - Versione"
++
+ msgid "OSD"
+ msgstr "OSD"
+
+@@ -487,6 +806,9 @@ msgstr "Stile interfaccia"
+ msgid "Setup.OSD$Theme"
+ msgstr "Tema colori"
+
++msgid "Setup.OSD$WarEagle icons"
++msgstr "Icone WarEagle"
++
+ msgid "Setup.OSD$Left"
+ msgstr "Sinistra"
+
+@@ -556,12 +878,30 @@ msgstr "Tasto Menu per chiudere"
+ msgid "Setup.OSD$Recording directories"
+ msgstr "Directory di registrazione"
+
++msgid "Setup.OSD$Main menu title"
++msgstr "Titolo menu principale"
++
++msgid "Setup.OSD$- Text"
++msgstr "- Testo"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Posizione comandi menu princ."
++
++msgid "Setup.OSD$Show valid input"
++msgstr "Mostra ingresso valido"
++
+ msgid "EPG"
+ msgstr "Guida programmi EPG"
+
+ msgid "Button$Scan"
+ msgstr "Scansione"
+
++msgid "blacklist"
++msgstr "lista nera"
++
++msgid "whitelist"
++msgstr "elenco autorizzati"
++
+ msgid "Setup.EPG$EPG scan timeout (h)"
+ msgstr "Scadenza aggiorn. EPG (ore)"
+
+@@ -571,6 +911,9 @@ msgstr "Livello correzione EPG"
+ msgid "Setup.EPG$EPG linger time (min)"
+ msgstr "Mostra vecchi dati EPG (min)"
+
++msgid "Setup.EPG$Show progress bar"
++msgstr "Mostra barra avanzamento"
++
+ msgid "Setup.EPG$Set system time"
+ msgstr "Imposta orario di sistema"
+
+@@ -585,6 +928,27 @@ msgstr "Lingue preferite"
+ msgid "Setup.EPG$Preferred language"
+ msgstr "Lingua preferita"
+
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "Tempo doppia ricerca EPG (min)"
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr "Doppio valore EPG esterno"
++
++msgid "adjust"
++msgstr "regola"
++
++msgid "delete"
++msgstr "elimina"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "Mescola EPG interno e esterno"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "Disabilita esec. evento VPS"
++
++msgid "Setup.EPG$Mode of noEPG-Patch"
++msgstr "Modalità di noEPG-Patch"
++
+ msgid "pan&scan"
+ msgstr "pan&scan"
+
+@@ -594,9 +958,6 @@ msgstr "letterbox"
+ msgid "center cut out"
+ msgstr "center cut out"
+
+-msgid "no"
+-msgstr "no"
+-
+ msgid "names only"
+ msgstr "solo nomi"
+
+@@ -615,6 +976,27 @@ msgstr "nuovi transponder"
+ msgid "DVB"
+ msgstr "Scheda DVB"
+
++msgid "qam256"
++msgstr "qam256"
++
++msgid "dvb-c"
++msgstr "dvb-c"
++
++msgid "dvb-s"
++msgstr "dvb-s"
++
++msgid "all"
++msgstr "tutti"
++
++msgid "has decoder"
++msgstr "con decoder"
++
++msgid "is primary"
++msgstr "primaria"
++
++msgid "has decoder + is primary"
++msgstr "con decoder e primaria"
++
+ msgid "Setup.DVB$Primary DVB interface"
+ msgstr "Scheda DVB primaria"
+
+@@ -654,9 +1036,28 @@ msgstr "Trasparenza sottotitoli"
+ msgid "Setup.DVB$Subtitle background transparency"
+ msgstr "Trasparenza sfondo sottotitoli"
+
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr "Utilizza correzione AC3-Transfer"
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr "Blocco canale"
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr "Modalità filtro blocco canale"
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr "Utilizza correzione Early Sync"
++
+ msgid "LNB"
+ msgstr "LNB"
+
++#, c-format
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr "La scheda DVB %d utilizza LNB No."
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr "Registra utilizzo LNB"
++
+ msgid "Setup.LNB$Use DiSEqC"
+ msgstr "Utilizza DiSEqC"
+
+@@ -699,6 +1100,9 @@ msgstr "La CAM è in uso - vuoi reimposta
+ msgid "Can't reset CAM!"
+ msgstr "Impossibile reimpostare il modulo CAM!"
+
++msgid "request"
++msgstr "chiedi"
++
+ msgid "Recording"
+ msgstr "Registrazione"
+
+@@ -723,9 +1127,29 @@ msgstr "Priorità di pausa"
+ msgid "Setup.Recording$Pause lifetime (d)"
+ msgstr "Durata pausa (gg)"
+
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "Registra Dolby Digital"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr "Regole directory video"
++
++msgid "Setup.Recording$Number of video directories"
++msgstr "Numero di directory video"
++
++#, c-format
++msgid "Setup.Recording$Video %d priority"
++msgstr "Priorità video %d "
++
++#, c-format
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr "Min. MB disponibili video %d "
++
+ msgid "Setup.Recording$Use episode name"
+ msgstr "Utilizza nome episodio"
+
++msgid "Setup.Recording$Friendly filenames"
++msgstr "Nomi file semplici"
++
+ msgid "Setup.Recording$Use VPS"
+ msgstr "Utilizza VPS"
+
+@@ -744,9 +1168,39 @@ msgstr "Durata reg. immediata (min)"
+ msgid "Setup.Recording$Max. video file size (MB)"
+ msgstr "Dim. massima file video (MB)"
+
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "Dim. massima reg. (GB)"
++
+ msgid "Setup.Recording$Split edited files"
+ msgstr "Dividi i file modificati"
+
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "Taglia collegamenti Hard"
++
++msgid "Setup.Recording$Delete timeshift recording"
++msgstr "Elimina reg. timeshift"
++
++msgid "Setup.Recording$Show date"
++msgstr "Mostra data registrazione"
++
++msgid "Setup.Recording$Show time"
++msgstr "Mostra ora registrazione"
++
++msgid "Setup.Recording$Show length"
++msgstr "Mostra durata registrazione"
++
++msgid "Setup.Recording$Show end of timer"
++msgstr "Mostra fine del timer"
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr "Ordina registrazioni per"
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr "Ordina directory prima di reg."
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr "Elimina taglio automatico"
++
+ msgid "Replay"
+ msgstr "Riproduzione"
+
+@@ -759,6 +1213,63 @@ msgstr "Mostra modalità riproduzione"
+ msgid "Setup.Replay$Resume ID"
+ msgstr "ID di ripristino"
+
++msgid "Setup.Replay$Jump&Play"
++msgstr "Vai a & Riproduci"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Riproduci & Vai a"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "Pausa all'ultimo marcatore"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "Ricarica marcatori"
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr "Salta secondi"
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr "Salta secondi lentamente"
++
++msgid "Setup.Replay$Length"
++msgstr "Durata"
++
++msgid "Setup.Replay$Length / Number"
++msgstr "Durata / Numero"
++
++msgid "Setup.Replay$Number"
++msgstr "Numero"
++
++msgid "Setup.Replay$DVD display mode"
++msgstr "Mod. visualizzazione DVD"
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr "Mostra zeri davanti al DVD"
++
++msgid "Setup.Replay$never"
++msgstr "mai"
++
++msgid "Setup.Replay$on begin"
++msgstr "all'inizio"
++
++msgid "Setup.Replay$on end"
++msgstr "alla fine"
++
++msgid "Setup.Replay$on begin and end"
++msgstr "all'inizio e alla fine"
++
++msgid "Setup.Replay$Tray open"
++msgstr "Apri vassoio"
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr "Limita velocità DVD a"
++
++msgid "Setup.Miscellaneous$only in channelinfo"
++msgstr "solo nelle info canale"
++
++msgid "Setup.Miscellaneous$only in progress display"
++msgstr "solo nel canale in esec."
++
+ msgid "Miscellaneous"
+ msgstr "Generici"
+
+@@ -786,9 +1297,54 @@ msgstr "come prima"
+ msgid "Setup.Miscellaneous$Initial volume"
+ msgstr "Volume iniziale"
+
++msgid "Setup.Miscellaneous$Volume ctrl with left/right"
++msgstr "Controllo volume con Sinistra/Destra"
++
++msgid "Setup.Miscellaneous$Channelgroups with left/right"
++msgstr "Gruppi canali con Sinistra/Destra"
++
++msgid "Setup.Miscellaneous$Search fwd/back with left/right"
++msgstr "Cerca avanti/indietro con Sinistra/Destra"
++
+ msgid "Setup.Miscellaneous$Emergency exit"
+ msgstr "Uscita di emergenza"
+
++msgid "Setup.Miscellaneous$Lirc repeat delay"
++msgstr "Ritardo ripetizione LIRC"
++
++msgid "Setup.Miscellaneous$Lirc repeat freq"
++msgstr "Frequenza ripetizione LIRC"
++
++msgid "Setup.Miscellaneous$Lirc repeat timeout"
++msgstr "Scadenza ripetizione LIRC"
++
++msgid "Permanent Timeshift"
++msgstr "Timeshift permanente"
++
++msgid "Setup.LiveBuffer$Location"
++msgstr "Posizione"
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr "Disco fisso"
++
++msgid "Setup.LiveBuffer$memory"
++msgstr "Memoria"
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr "Dim. buffer in memoria (MB)"
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr "Utilizza disco fisso se necessario"
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr "Mantieni buffer di pausa"
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr "Dim. buffer nel disco fisso (MB)"
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr "Dimensione buffer (MB)"
++
+ msgid "Plugins"
+ msgstr "Plugins"
+
+@@ -814,7 +1370,6 @@ msgstr "Programmi"
+ msgid "VDR"
+ msgstr "VDR"
+
+-#. TRANSLATORS: note the leading blank!
+ msgid " Stop replaying"
+ msgstr " Ferma riproduzione"
+
+@@ -830,7 +1385,6 @@ msgstr "Ferma"
+ msgid "Button$Resume"
+ msgstr "Riprendi"
+
+-#. TRANSLATORS: note the leading blank!
+ msgid " Cancel editing"
+ msgstr " Annulla modifiche"
+
+@@ -861,10 +1415,22 @@ msgstr "Nessuna periferica DVB disponibi
+ msgid "Pausing live video..."
+ msgstr "Pausa del canale in visione..."
+
++msgid "Jump"
++msgstr "Vai a"
++
++msgid "Skip +60s"
++msgstr "Salta +60s"
++
++msgid "Skip -60s"
++msgstr "Salta - 60s"
++
+ #. TRANSLATORS: note the trailing blank!
+ msgid "Jump: "
+ msgstr "Vai a: "
+
++msgid "Cutter already running - Add to cutting queue?"
++msgstr "Taglio in esecuzione - Aggiungere alla coda tagli?"
++
+ msgid "No editing marks defined!"
+ msgstr "Nessun marcatore di modifica definito!"
+
+@@ -880,9 +1446,6 @@ msgstr "Processo di modifica già attivo!
+ msgid "FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
+ msgstr " aáàbcdeéèfghiìîjklmnoòpqrstuùvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&°"
+
+-msgid "yes"
+-msgstr "sì"
+-
+ msgid "CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
+ msgstr " 0\t-.,1#~\\^$[]|()*+°?{}/:%@&\taàbc2\tdeèf3\tghiì4\tjkl5\tmnoò6\tpqrs7\ttuùv8\twxyz9"
+
+@@ -895,6 +1458,18 @@ msgstr "Sovrascrivi"
+ msgid "Button$Insert"
+ msgstr "Inserisci"
+
++msgid "Rename$Up"
++msgstr "Su"
++
++msgid "Rename$Down"
++msgstr "Giù"
++
++msgid "Rename$Previous"
++msgstr "Precedente"
++
++msgid "Rename$Next"
++msgstr "Successivo"
++
+ msgid "Plugin"
+ msgstr "Plugin"
+
+@@ -907,6 +1482,30 @@ msgstr "Canale bloccato (in registrazion
+ msgid "Low disk space!"
+ msgstr "Poco spazio su disco!"
+
++#, c-format
++msgid "Please mount %s"
++msgstr "Monta %s"
++
++#, c-format
++msgid "Please mount DVD %04d"
++msgstr "Monta il DVD %04d"
++
++#, c-format
++msgid "Please mount DVD %d"
++msgstr "Monta il DVD %d"
++
++msgid "Please wait. Checking DVD..."
++msgstr "Attendere prego. Verifica DVD..."
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr "Nessun indice trovato. La creazione può impiegare alcuni minuti. Crearne uno?"
++
++msgid "Please wait. Creating index-file..."
++msgstr "Attendere prego. Creazione indice..."
++
++msgid "Wrong DVD!"
++msgstr "DVD errato!"
++
+ msgid "Can't shutdown - option '-s' not given!"
+ msgstr "Impossibile spegnere - parametro '-s' non assegnato!"
+
+@@ -978,9 +1577,6 @@ msgstr "Domenica"
+ msgid "Upcoming recording!"
+ msgstr "Registrazione imminente!"
+
+-msgid "Recording started"
+-msgstr "Registrazione avviata"
+-
+ msgid "VDR will shut down later - press Power to force"
+ msgstr "VDR si spegnerà più tardi - premi Power per forzare"
+
+@@ -1002,3 +1598,4 @@ msgstr "Premi un tasto per annullare il
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR si spegnerà tra %s minuti"
++
+diff -ruNp vdr-1.6.0-2/po/nl_NL.po vdr-1.6.0-2-extensions/po/nl_NL.po
+--- vdr-1.6.0-2/po/nl_NL.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/nl_NL.po 2009-04-09 20:48:26.000000000 +0200
+@@ -1002,3 +1002,270 @@ msgstr "Druk een willekeurige toets om h
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR zal na %s minuten uitschakelen"
++
++msgid "Channel locked by LNB!"
++msgstr "Kanaal geblokkeerd door LNB"
++
++msgid "Childlock"
++msgstr "Kinderslot"
++
++msgid "Path"
++msgstr "Pad"
++
++msgid "Timer commands"
++msgstr "Timer commando's"
++
++msgid "Rename recording"
++msgstr "Hernoem opnamen"
++
++msgid "Date"
++msgstr "Datum"
++
++msgid "Length"
++msgstr "Lengte"
++
++msgid "Size"
++msgstr "Grootte"
++
++msgid "Delete marks information?"
++msgstr "Verwijder informatiemarkeringen?"
++
++msgid "Delete resume information?"
++msgstr "Verwijder hervattingsmarkeringen?"
++
++msgid "DVD plugin is not installed!"
++msgstr "DVd plugin is niet geÃnstalleerd!"
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr "hoofdmap alfabetisch, submappen flexibel"
++
++msgid "main dir by date, subdirs flexible"
++msgstr "hoofdmap op datum, submappen flexibel"
++
++msgid "all alphabetically"
++msgstr "alles alfabetisch"
++
++msgid "all by date"
++msgstr "alles op datum"
++
++msgid "Sorting"
++msgstr "Sortering"
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr "WarEagle symbolen"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Positie commandobalk in hoofdmenu"
++
++msgid "Setup.OSD$Show valid input"
++msgstr "Toon geldige invoer"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Toon voortschreidingsbalk"
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "Tijdsduur starten dubbele EPG zoekactie (min)"
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr "externe dubbele EPG invoer"
++
++msgid "adjust"
++msgstr "afstellen"
++
++msgid "delete"
++msgstr "verwijderen"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "Mix in- en externe EPG"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "Schakel aktieve VPS uitzending uit"
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr "Gebruik AC3-Transfer Fix"
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr "Gebruik 'Sync Early' Patch"
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr "LNB kaart %d gebruikt LNB Nr."
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr "Houd LNB gebruik bij"
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "Neem Dolby Digital spoor op"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr "Omgang m.b.t. Videomappen"
++
++msgid "Setup.Recording$Number of video directories"
++msgstr "Aantal videomappen"
++
++msgid "Setup.Recording$Video %d priority"
++msgstr "Video %d prioriteit"
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr "Video %d min. vrij MB"
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr "Handzame bestandsnamen"
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "Max. opnamegrootte (GB)"
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "'Hard link' bestandsbewerker"
++
++msgid "Setup.Recording$Show date"
++msgstr "Toon datum"
++
++msgid "Setup.Recording$Show time"
++msgstr "Toon tijd"
++
++msgid "Setup.Recording$Show length"
++msgstr "Toon lengte"
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr "Toon einde van timers"
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr "Sorteer opnames op"
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr "Sorteer mappen vÃÃr opnames"
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr "Bestandsbewerker automatisch wissen"
++
++msgid "Setup.Replay$Jump&Play"
++msgstr "Spring&Speel"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Speel&Spring"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "Pauzeer bij laatste markering"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "Herlaadt markeringen"
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr "Spring seconden"
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr "Spring seconden langzaam"
++
++msgid "Setup.Replay$Length"
++msgstr "Lengte"
++
++msgid "Setup.Replay$Length / Number"
++msgstr "Lengte / Nummer"
++
++msgid "Setup.Replay$Number"
++msgstr "Nummer"
++
++msgid "Setup.Replay$DVD display mode"
++msgstr "DVD displayinstellingen"
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr "DVD toon voorloopnullen"
++
++msgid "Setup.Replay$never"
++msgstr "nooit"
++
++msgid "Setup.Replay$on begin"
++msgstr "aan het begin"
++
++msgid "Setup.Replay$on end"
++msgstr "bij het einde"
++
++msgid "Setup.Replay$on begin and end"
++msgstr "bij het begin en aan het einde"
++
++msgid "Setup.Replay$Tray open"
++msgstr "Open DVD lade"
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr "Beperk DVD omw. snelheid"
++
++msgid "Jump"
++msgstr "Spring"
++
++msgid "Skip +60s"
++msgstr "Spring +60s verder"
++
++msgid "Skip -60s"
++msgstr "Spring -60s terug"
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr "Bestandsbewerker aktief - aan wachtrij toeveogen?"
++
++msgid "Rename$Up"
++msgstr "Boven"
++
++msgid "Rename$Down"
++msgstr "Onder"
++
++msgid "Rename$Previous"
++msgstr "Vorige"
++
++msgid "Rename$Next"
++msgstr "Volgende"
++
++msgid "Please mount %s"
++msgstr "Koppel %s aan a.u.b."
++
++msgid "Please mount DVD %04d"
++msgstr "Koppel DVD %04d aan a.u.b."
++
++msgid "Please mount DVD %d"
++msgstr "Koppel DVD %d aan a.u.b."
++
++msgid "Please wait. Checking DVD..."
++msgstr "Even wachten, DVD wordt gecontroleerd..."
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr "Geen indexbestand gevonden. Aanmaken duurt even, doorgaan?"
++
++msgid "Please wait. Creating index-file..."
++msgstr "Even wachten indexbestand wordt aangemaakt..."
++
++msgid "Wrong DVD!"
++msgstr "Verkeerde DVD!"
++
++msgid "Permanent Timeshift"
++msgstr "Permanente livebuffer"
++
++msgid "Setup.LiveBuffer$Location"
++msgstr "Opslaglocatie"
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr "hardeschijf"
++
++msgid "Setup.LiveBuffer$memory"
++msgstr "geheugen"
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr "Buffergrootte in geheugen (MB)"
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr "Overlopen naar hardeschijf indien noodzakelijk"
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr "Bewaar gepauzeerde buffer"
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr "Buffergrootte op harddisk (MB)"
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr "Buffergrootte (MB)"
+diff -ruNp vdr-1.6.0-2/po/nn_NO.po vdr-1.6.0-2-extensions/po/nn_NO.po
+--- vdr-1.6.0-2/po/nn_NO.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/nn_NO.po 2009-04-09 20:48:26.000000000 +0200
+@@ -999,3 +999,270 @@ msgstr ""
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr ""
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/pl_PL.po vdr-1.6.0-2-extensions/po/pl_PL.po
+--- vdr-1.6.0-2/po/pl_PL.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/pl_PL.po 2009-04-09 20:48:26.000000000 +0200
+@@ -999,3 +999,270 @@ msgstr "Naci¶nij dowolny klawisz aby nie
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR zostanie wy³±czony za %s minut"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/pt_PT.po vdr-1.6.0-2-extensions/po/pt_PT.po
+--- vdr-1.6.0-2/po/pt_PT.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/pt_PT.po 2009-04-09 20:48:26.000000000 +0200
+@@ -998,3 +998,270 @@ msgstr "Pressione qualquer tecla para nã
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR vai desligar em %s minutos"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/ro_RO.po vdr-1.6.0-2-extensions/po/ro_RO.po
+--- vdr-1.6.0-2/po/ro_RO.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/ro_RO.po 2009-04-09 20:48:26.000000000 +0200
+@@ -1001,3 +1001,270 @@ msgstr "Apãsaþi orice tastã pentru a anu
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR se va închide în %s minute"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/ru_RU.po vdr-1.6.0-2-extensions/po/ru_RU.po
+--- vdr-1.6.0-2/po/ru_RU.po 2008-03-23 11:31:29.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/ru_RU.po 2009-04-09 20:48:26.000000000 +0200
+@@ -999,3 +999,271 @@ msgstr "½ÐÖÜØâÕ ÛîÑãî ÚÝÞßÚã ÔÛï ÞâÜÕÝë
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR ÒëÚÛîçØâáï çÕàÕ× %s ÜØÝãâ"
++
++msgid "Channel locked by LNB!"
++msgstr "ºÞÝÒÕàâÕà ÑÛÞÚØàãÕâ ÚÐÝÐÛ"
++
++msgid "Childlock"
++msgstr "´ÕâáÚÐï ÑÛÞÚØàÞÒÚÐ"
++
++msgid "Path"
++msgstr "¿ãâì"
++
++msgid "Timer commands"
++msgstr "ºÞÜÜÐÝÔë âÐÙÜÕàÐ"
++
++msgid "Rename recording"
++msgstr "¿ÕàÕØÜÕÝÞÒÐâì ×Ðߨáì"
++
++msgid "Date"
++msgstr "´ÐâÐ"
++
++msgid "Length"
++msgstr "´ÛØÝÐ"
++
++msgid "Size"
++msgstr "ÀÐ×ÜÕà"
++
++msgid "Delete marks information?"
++msgstr "ÃÔÐÛØâì ÜÕâÚØ?"
++
++msgid "Delete resume information?"
++msgstr "²ÞááâÐÝÞÒØâì ÜÕâÚØ?"
++
++msgid "DVD plugin is not installed!"
++msgstr "¼ÞÔãÛì DVD ÝÕ ãáâÐÝÞÒÛÕÝ!"
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr "ÓÛÐÒÝÐï ÔØàÕÚâÞàØï ßÞ ÐÛäÐÒØâã, ßÞÔÔØàÕÚâÞàØØ ÓØÑÚÞ"
++
++msgid "main dir by date, subdirs flexible"
++msgstr "ÓÛÐÒÝÐï ÔØàÕÚâÞàØï ßÞ ÒàÕÜÕÝØ, ßÞÔÔØàÕÚâÞàØØ ÓØÑÚÞ"
++
++msgid "all alphabetically"
++msgstr "ÒáÕ ßÞ ÐÛäÐÒØâã"
++
++msgid "all by date"
++msgstr "ÒáÕ ßÞ ÒàÕÜÕÝØ"
++
++msgid "Sorting"
++msgstr "ÁÞàâØàÞÒÚÐ"
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr "WarEagle ØÚÞÝÚØ"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "ÀÐ×ÜÕéÕÝØÕ ÚÞÜÐÝÔ Ò ÓÛÐÒÝÞÜ ÜÕÝî"
++
++msgid "Setup.OSD$Show valid input"
++msgstr "¿ÞÚÐ× ßàÐÒØÛìÝÞÓÞ ÒÒÞÔÐ"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "¿ÞÚÐ× ÑÐÛÚØ ßàÞÓàÕááÐ"
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "¿ÕàØÞÔ ÔÛï ßÞØáÚÐ ÔÒÞÙÝëå EPG(min)"
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr "²ÝÕèÝØÙ ØáâÞçÝØÚ ÔÒÞÙÝÞÓÞ EPG"
++
++msgid "adjust"
++msgstr "ÝÐáâàÞØâì"
++
++msgid "delete"
++msgstr "ãÔÐÛØâì"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "ÁÜÕèØÒÐÝØÕ ÒÝãâàÕÝÝÕÓÞ Ø ÒÝÕèÝÕÓÞ EPG"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "·ÐßàÕâ VPS áÞÑëâØï"
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr "¸áßÞÛì×ÞÒÐâì ÚÞààÕÚâØàÞÒÚã AC3-Transfer"
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr "¸áßÞÛì×ÞÒÐâì Sync Early "
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr "DVB ãáâàÞÙáâÒÞ %d ØáßÞÛì×ãÕâ LNB No."
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr "»ÞÓØàÞÒÐÝØÕ ØáßÞÛì×ÞÒÐÝØï LNB"
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "·Ðߨáì Dolby Digital"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr "¿ÞàïÔÞÚ ÒØÔÕÞ ÔØàÕÚâÞàØØ"
++
++msgid "Setup.Recording$Number of video directories"
++msgstr "ºÞÛØçÕáâÒÞ ÒØÔÕÞ ÔØàÕÚâÞàØÙ"
++
++msgid "Setup.Recording$Video %d priority"
++msgstr "Video %d ßàØÞàØâÕâ"
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr "Video %d ÜØÝ. áÒÞÑÞÔÝÞ MB"
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr "ÇØâÐÑÕÛìÝëÕ ØÜÕÝÐ äÐÙÛÞÒ"
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "¼ÐÚá. àÐ×ÜÕà ×ÐßØáØ (GB)"
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "¼ÞÝâÐÖ á hard link"
++
++msgid "Setup.Recording$Show date"
++msgstr "¿ÞÚÐ×ëÒÐâì ÔÐâã"
++
++msgid "Setup.Recording$Show time"
++msgstr "¿ÞÚÐ×ëÒÐâì ÒàÕÜï ×ÐߨáØ"
++
++msgid "Setup.Recording$Show length"
++msgstr "¿ÞÚÐ×ëÒÐâì ßàÞÔÞÛÖØâÕÛìÝÞáâì ×ÐߨáØ"
++
++msgid "Setup.OSD$Main menu title"
++msgstr "½Ð×ÒÐÝØÕ ÓÛÐÒÝÞÓÞ ÜÕÝî"
++
++msgid "Setup.Recording$Show end of timer"
++msgstr "¿ÞÚÐ× ÞÚÞÝçÐÝØï âÐÙÜÕàÐ"
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr "ÁÞàâØàÞÒÚÐ ×ÐߨáÕÙ ßÞ"
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr "ÁÞàâØàÞÒÚÐ ÔØàÕÚâÞàØÙ ÔÞ ×ÐߨáØ"
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr "°ÒâÞÜÐâØçÕáÚÞÕ ãÔÐÛÕÝØÕ ÜÞÝâÐÖÐ"
++
++msgid "Setup.Replay$Jump&Play"
++msgstr "Jump&Play"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Play&Jump"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "¿Ðã×Ð ÝÐ ßÞáÛÕÔÝÕÙ ÜÕâÚÕ"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "¿ÕàÕÓàãרâì ÜÕâÚØ"
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr "¿àÞßãáÚ áÕÚãÝÔ"
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr "¿àÞßãáÚ áÕÚãÝÔ ÜÕÔÛÕÝÝÞ"
++
++msgid "Setup.Replay$Length"
++msgstr "´ÛØÝÐ"
++
++msgid "Setup.Replay$Length / Number"
++msgstr "´ÛØÝÐ / ½ÞÜÕà"
++
++msgid "Setup.Replay$Number"
++msgstr "½ÞÜÕà"
++
++msgid "Setup.Replay$DVD display mode"
++msgstr "ÀÕÖØÜ ßÞÚÐ×Ð DVD"
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr "ßÞÚÐ× ÒÕÔãéØå ÝãÛÕÙ"
++
++msgid "Setup.Replay$never"
++msgstr "ÝØÚÞÓÔÐ"
++
++msgid "Setup.Replay$on begin"
++msgstr "Ò ÝÐçÐÛÕ"
++
++msgid "Setup.Replay$on end"
++msgstr "² ÚÞÝæÕ"
++
++msgid "Setup.Replay$on begin and end"
++msgstr "Ò ÝÐçÐÛÕ Ø Ò ÚÞÝæÕ"
++
++msgid "Setup.Replay$Tray open"
++msgstr "¾âÚàëâì"
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr "¿àÕÔÕÛ áÚÞàÞáâØ DVD"
++
++msgid "Jump"
++msgstr "¿àëÖÞÚ"
++
++msgid "Skip +60s"
++msgstr "¿àÞßãáÚ +60s"
++
++msgid "Skip -60s"
++msgstr "¿àÞßãáÚ -60s"
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr "¼ÞÝâÐÖÕà àÐÑÞâÐÕâ - ´ÞÑÐÒØâì Ò ÞçÕàÕÔì?"
++
++msgid "Rename$Up"
++msgstr "²ÒÕàå"
++
++msgid "Rename$Down"
++msgstr "²ÝØ×"
++
++msgid "Rename$Previous"
++msgstr "¿àÕÔëÔãéØÙ"
++
++msgid "Rename$Next"
++msgstr "ÁÛÕÔãîéØÙ"
++
++msgid "Please mount %s"
++msgstr "¿ÞÖÐÛãÙáâÐ ßÞÔÜÞÝâØàãÙâÕ %s"
++
++msgid "Please mount DVD %04d"
++msgstr "¿ÞÖÐÛãÙáâÐ ßÞÔÜÞÝâØàãÙâÕ DVD %04d"
++
++msgid "Please mount DVD %d"
++msgstr "¿ÞÖÐÛãÙáâÐ ßÞÔÜÞÝâØàãÙâÕ DVD %d"
++
++msgid "Please wait. Checking DVD..."
++msgstr "¿ÞÖÐÛãÙáâÐ ßÞÔÞÖÔØâÕ. ¿àÞÒÕàÚÐ DVD..."
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr "¸ÝÔÕÚá äÐÙÛ ÝÕ ÝÐÙÔÕÝ. ÁÞ×ÔÐÝØÕ âàÕÑãÕâ ÒàÕÜÕÝØ. ÁÞ×ÔÐâì?"
++
++msgid "Please wait. Creating index-file..."
++msgstr "¿ÞÖÐÛãÙáâÐ ßÞÔÞÖÔØâÕ. ÁÞ×ÔÐÝØÕ ØÝÔÕÚá äÐÙÛÐ..."
++
++msgid "Wrong DVD!"
++msgstr "¾èØÑÚÐ DVD!"
++
++msgid "Permanent Timeshift"
++msgstr "¿ÞáâÞïÝÝëÙ âÐÙÜèØäâ"
++
++msgid "Setup.LiveBuffer$Location"
++msgstr "¼ÕáâÞÝÐåÞÖÔÕÝØï"
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr "ÖÕáâÚØÙ ÔØáÚ"
++
++msgid "Setup.LiveBuffer$memory"
++msgstr "ßÐÜïâì"
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr "ÀÐ×ÜÕà ÑãäÕàÐ Ò ßÐÜïâØ (MB)"
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr "´ÞÑÐÒÛïâì ÖÕáâÚØÙ ÔØáÚ ÕáÛØ ÝÕÞÑåÞÔØÜÞ"
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr "ÅàÐÝÕÝØÕ ÑãäÕàÐ ßÐã×ë"
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr "ÀÐ×ÜÕà ÑãäÕàÐ ÝÐ ÖÕáâÚÞÜ ÔØáÚÕ (MB)"
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr "ÀÐ×ÜÕà ÑãäÕàÐ (MB)"
++
+diff -ruNp vdr-1.6.0-2/po/sl_SI.po vdr-1.6.0-2-extensions/po/sl_SI.po
+--- vdr-1.6.0-2/po/sl_SI.po 2008-03-23 11:31:30.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/sl_SI.po 2009-04-09 20:48:26.000000000 +0200
+@@ -999,3 +999,270 @@ msgstr "Pritisni katerokoli tipko za pre
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR se bo zaustavil v %s minutah"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/sv_SE.po vdr-1.6.0-2-extensions/po/sv_SE.po
+--- vdr-1.6.0-2/po/sv_SE.po 2008-03-23 11:31:30.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/sv_SE.po 2009-04-09 20:48:26.000000000 +0200
+@@ -1001,3 +1001,270 @@ msgstr "Tryck valfri knapp för att återk
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR kommer att stängas ned om %s minuter"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/po/tr_TR.po vdr-1.6.0-2-extensions/po/tr_TR.po
+--- vdr-1.6.0-2/po/tr_TR.po 2008-03-23 11:31:30.000000000 +0100
++++ vdr-1.6.0-2-extensions/po/tr_TR.po 2009-04-09 20:48:26.000000000 +0200
+@@ -998,3 +998,270 @@ msgstr "Yeniden baþlatmayý iptal etmek i
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR %s dakikada kapanacak"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr ""
++
++msgid "Timer commands"
++msgstr ""
++
++msgid "Rename recording"
++msgstr ""
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr ""
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.DVB$Use Sync Early Patch"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr ""
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr ""
++
++msgid "Setup.Recording$Show time"
++msgstr ""
++
++msgid "Setup.Recording$Show length"
++msgstr ""
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Rename$Up"
++msgstr ""
++
++msgid "Rename$Down"
++msgstr ""
++
++msgid "Rename$Previous"
++msgstr ""
++
++msgid "Rename$Next"
++msgstr ""
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Permanent Timeshift"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Location"
++msgstr ""
++
++msgid "Setup.LiveBuffer$harddisk"
++msgstr ""
++
++msgid "Setup.LiveBuffer$memory"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size in memory (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Extend to harddisk if necessary"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Keep paused Buffer"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size on harddisk (MB)"
++msgstr ""
++
++msgid "Setup.LiveBuffer$Buffer size (MB)"
++msgstr ""
+diff -ruNp vdr-1.6.0-2/README.cmdsubmenu vdr-1.6.0-2-extensions/README.cmdsubmenu
+--- vdr-1.6.0-2/README.cmdsubmenu 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/README.cmdsubmenu 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,54 @@
++CmdSubmenu patch for VDR
++------------------------
++
++With this patch the commands and recording commands menus can be organised
++hierarchically. To create a submenu entry, prefix the name by one ore more "-".
++
++
++Standard:
++
++description_1 : cmd_1
++description_2 : cmd_2
++
++
++A submenu with two entries:
++
++Submenu title ... : echo "submenu"
++-description_1 : cmd_1
++-description_2 : cmd_2
++
++The dummy command in the title row is necessary.
++
++
++* History
++
++ 2003-10-08: Version 0.1 - Albu at vdrportal.de
++ http://vdrportal.de/board/thread.php?threadid=6319
++
++ 2003-10-09: Version 0.2 - Tobias Grimm <tg@e-tobi.net>
++ - Added Define CMD_SUBMENUS in Makefile
++
++ 2004-05-28: Version 0.3 - Thomas Günther <tom@toms-cafe.de>
++ - Fixed compilation with gcc-3.3.3
++ - Added new virtual method AddConfig in cConfig
++ - Redefining of method Add in cListBase to virtual no longer necessary
++ - Improved code in menu.c
++ http://toms-cafe.de/vdr/download/vdr-cmdsubmenu-0.3.diff
++
++ 2004-12-20: Version 0.4 - Thomas Günther <tom@toms-cafe.de>
++ - Solved conflict with jumpplay patch 0.6
++ http://toms-cafe.de/vdr/download/vdr-cmdsubmenu-0.4.diff
++
++ 2006-04-22: Version 0.5 - Thomas Günther <tom@toms-cafe.de>
++ - Added version define CMDSUBMENUVERSNUM
++ - Reformated to VDR style indentions
++ - Added description in README.cmdsubmenu
++ http://toms-cafe.de/vdr/download/vdr-cmdsubmenu-0.5-1.3.47.diff
++
++ 2006-04-23: Version 0.6 - Thomas Günther <tom@toms-cafe.de>
++ - Fixed menus with more than one level
++ http://toms-cafe.de/vdr/download/vdr-cmdsubmenu-0.6-1.3.47.diff
++
++ 2006-05-15: Version 0.7 - Thomas Günther <tom@toms-cafe.de>
++ - Fixed build with G++ 4.1 (extra qualification)
++ http://toms-cafe.de/vdr/download/vdr-cmdsubmenu-0.7-1.4.0.diff
+diff -ruNp vdr-1.6.0-2/README-HLCUTTER vdr-1.6.0-2-extensions/README-HLCUTTER
+--- vdr-1.6.0-2/README-HLCUTTER 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/README-HLCUTTER 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,117 @@
++
++ VDR-HLCUTTER README
++
++
++Written by: Udo Richter
++Available at: http://www.udo-richter.de/vdr/patches.html#hlcutter
++ http://www.udo-richter.de/vdr/patches.en.html#hlcutter
++Contact: udo_richter@gmx.de
++
++
++
++About
++-----
++
++The hard link cutter patch changes the recording editing algorithms of VDR to
++use filesystem hard links to 'copy' recording files whenever possible to speed
++up editing recordings noticeably.
++
++The patch has matured to be quite stable, at least I'm using it without issues.
++Nevertheless the patch is still in development and should be used with caution.
++The patch is EXPERIMENTAL for multiple /videoxx folders. The safety checks
++should prevent data loss, but you should always carefully check the results.
++
++While editing a recording, the patch searches for any 00x.vdr files that dont
++contain editing marks and would normally be copied 1:1 unmodified to the edited
++recording. In this case the current target 00x.vdr file will be aborted, and
++the cutter process attempts to duplicate the source file as a hard link, so
++that both files share the same disk space. If this succeeds, the editing
++process fast-forwards through the duplicated file and continues normally
++beginning with the next source file. If hard linking fails, the cutter process
++continues with plain old copying. (but does not take up the aborted last file.)
++
++After editing, the un-edited recording can be deleted as usual, the hard linked
++copies will continue to exist as the only remaining copy.
++
++To be effective, the default 'Max. video file size (MB)' should be lowered.
++The patch lowers the smallest possible file size to 1mb. Since VDR only
++supports up to 255 files, this would limit the recording size to 255Mb or
++10 minutes, in other words: This setting is insane!
++
++To make sure that the 255 file limit will not be reached, the patch also
++introduces "Max. recording size (GB)" with a default of 100Gb (66 hours), and
++increases the file size to 2000Mb early enough, so that 100Gb-recordings will
++fit into the 255 files.
++
++Picking the right parameters can be tricky. The smaller the file size, the
++faster the editing process works. However, with a small file size, long
++recordings will fall back to 2000Mb files soon, that are slow on editing again.
++
++Here are some examples:
++
++Max file size: 100Gb 100Gb 100Gb 100Gb 100Gb 100Gb 100Gb
++Max recording size: 1Mb 10Mb 20Mb 30Mb 40Mb 50Mb 100Mb
++
++Small files: 1-203 1-204 1-205 1-206 1-207 1-209 1-214
++ GBytes: 0.2 2.0 4.0 6.0 8.1 10.2 20.9
++ Hours: 0.13 1.3 2.65 4 5.4 6.8 13.9
++
++Big (2000mb) files: 204-255 204-255 206-255 207-255 208-255 210-255 215-255
++ GBytes: 101.5 99.6 97.7 95.7 93.8 89.8 80.1
++ Hours: 67 66 65 63 62 60 53
++
++A recording limit of 100Gb keeps plenty of reserve without blocking too much
++file numbers. And with a file size of 30-40Mb, recordings of 4-5 hours fit into
++small files completely. (depends on bit rate of course)
++
++
++
++The patch must be enabled in Setup-> Recordings-> Hard Link Cutter. When
++disabled, the cutter process behaves identical to VDR's default cutter.
++
++There's a //#define HARDLINK_TEST_ONLY in the videodir.c file that enables a
++test-mode that hard-links 00x.vdr_ files only, and continues the classic
++editing. The resulting 00x.vdr and 00x.vdr_ files should be identical. If you
++delete the un-edited recording, dont forget to delete the *.vdr_ files too,
++they will now eat real disk space.
++
++Note: 'du' displays the disk space of hard links only on first appearance, and
++usually you will see a noticeably smaller size on the edited recording.
++
++
++History
++-------
++Version 0.2.0
++
++ New: Support for multiple /videoXX recording folders, using advanced searching
++ for matching file systems where a hard link can be created.
++ Also supports deep mounted file systems.
++ Fix: Do not fail if last mark is a cut-in. (Again.)
++
++Version 0.1.4
++ New: Dynamic increase of file size before running out of xxx.vdr files
++ Fix: Last edit mark is not a cut-out
++ Fix: Write error if link-copied file is smaller than allowed file size
++ Fix: Broken index/marks if cut-in is at the start of a new file
++ Fix: Clear dangeling pointer to free'd cUnbufferedFile,
++ thx to Matthias Schwarzott
++
++Version 0.1.0
++ Initial release
++
++
++
++
++Future plans
++------------
++
++Since original and edited copy share disk space, free space is wrong if one of
++them is moved to *.del. Free space should only count files with hard link
++count = 1. This still goes wrong if all copies get deleted.
++
++
++For more safety, the hard-linked files may be made read-only, as modifications
++to one copy will affect the other copy too. (except deleting, of course)
++
++
++SetBrokenLink may get lost on rare cases, this needs some more thoughts.Index: vdr-1.5.9/README.jumpplay
+diff -ruNp vdr-1.6.0-2/README.jumpplay vdr-1.6.0-2-extensions/README.jumpplay
+--- vdr-1.6.0-2/README.jumpplay 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/README.jumpplay 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,92 @@
++JumpPlay patch for VDR
++----------------------
++
++This patch changes the replay behaviour for recordings that contain editing
++marks. It allows to immediately continue the replay after jumping forward to
++the next mark, and to automatically jump over the commercial break to the next
++"start" mark, if an "end" mark is reached.
++
++The features of this patch can be turned on or off with parameters in the replay
++setup. See MANUAL for description of this parameters: "Jump&Play", "Play&Jump",
++"Pause at last mark" and "Reload marks".
++
++
++* History
++
++ 2003-07-04: jumpandrun.diff - the Noad <theNoad@SoftHome.net>
++ Jump&Play
++
++ 2003-12-06: Version 0.0 - Torsten Kunkel <vdr@tkunkel.de>
++ Play&Jump (only if progressbar is visible)
++ Setup parameters Jump&Play and Play&Jump in the replay setup
++
++ 2004-01-20: Version 0.1 - Thomas Günther <tom@toms-cafe.de>
++ Jump&Play:
++ - fixed speed after jump
++ - fixed removing of marks
++ Play&Jump:
++ - jump only on "end" marks
++
++ 2004-01-27: Version 0.2 - Thomas Günther <tom@toms-cafe.de>
++ Jump&Play:
++ - fixed double jump
++ Play&Jump:
++ - fixed mark detection: fuzzy detection (until 3 seconds after mark)
++ - jump without progressbar
++ - mode "progressbar only" for old behaviour
++
++ 2004-01-31: Version 0.3 - Thomas Günther <tom@toms-cafe.de>
++ Jump&Play:
++ - fixed display frames
++ Play&Jump:
++ - fixed end of playing at last mark
++
++ 2004-07-11: Version 0.4 - Thomas Günther <tom@toms-cafe.de>
++ Jump&Play:
++ - don't play after jump to end
++ Play&Jump:
++ - don't prevent jumping after hide or show
++ Less conflicts with other patches (Elchi/AutoPID)
++
++ 2004-08-21: Version 0.5 - Thomas Günther <tom@toms-cafe.de>
++ Play&Jump:
++ - exact jumps, replay like edited recording (no fuzzy mark detection)
++ - jump to first mark if replay starts at the beginning
++ - check jump marks with '8' key
++ - mode "progressbar only" removed
++ Description in README.jumpplay
++
++ 2004-12-28: Version 0.6 - Thomas Günther <tom@toms-cafe.de>
++ Adapted noad extensions (from the Noad <theNoad@SoftHome.net>) to
++ jumpplay-0.5:
++ - cyclic reloading of marks found by noad online-scan
++ - don't stop after the last mark in case of live-recordings
++ New setup parameter "Load marks interval (s)"
++ Updated description in README.jumpplay
++
++ 2006-04-14: Version 0.7 - Thomas Günther <tom@toms-cafe.de>
++ Fixed jump to first mark (crashed with plugin extrecmenu-0.9)
++ Added version define JUMPPLAYVERSNUM
++ Added placeholders for Czech language texts
++ Cleaned up i18n entries (support only VDR >= 1.3.29)
++ Improved description of i18n placeholders - hoping for real language texts
++
++ 2006-05-12: Version 0.8 - Thomas Günther <tom@toms-cafe.de>
++ Fixed segfault in dvbplayer thread while the replaycontrol thread is
++ reloading the marks (thanks to horchi at vdrportal.de for reporting this -
++ see http://vdrportal.de/board/thread.php?postid=450463#post450463):
++ New class cMarksReload checks the timestamp of marks.vdr in 10 seconds
++ intervals, so the marks in the threads dvbplayer and replaycontrol can be
++ reloaded independently
++ Changed setup parameter "Load marks interval (s)" to "Reload marks"
++ Updated description in README.jumpplay
++
++ 2006-05-28: Version 0.9 - Thomas Günther <tom@toms-cafe.de>
++ New setup parameter "Pause at last mark"
++ Updated description in README.jumpplay
++ Moved parameters description to MANUAL
++
++ 2009-03-31: Version 1.0 - Thomas Günther <tom@toms-cafe.de>
++ Play&Jump:
++ - set resume position to 0 if replay stops at the first mark
++ Added French language texts (thanks to Michaël Nival)
+diff -ruNp vdr-1.6.0-2/README.MainMenuHooks vdr-1.6.0-2-extensions/README.MainMenuHooks
+--- vdr-1.6.0-2/README.MainMenuHooks 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/README.MainMenuHooks 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,55 @@
++This is a "patch" for the Video Disk Recorder (VDR).
++
++* Authors:
++Tobias Grimm <vdr at e-tobi dot net>
++Martin Prochnow <nordlicht at martins-kabuff dot de>
++Frank Schmirler <vdrdev at schmirler dot de>
++Christian Wieninger <cwieninger at gmx dot de>
++
++* Description:
++This patch allows plugins to replace the VDR mainmenus "Schedule",
++"Channels", "Timers" and "Recordings" by a different implementation.
++
++The patch is based on a suggestion of Christian Wieninger back in 2006
++(http://www.linuxtv.org/pipermail/vdr/2006-March/008234.html). It is
++meant to be an interim solution for VDR 1.4 until (maybe) VDR 1.5
++introduces an official API for this purpose.
++
++* Installation
++Change into the VDR source directory, then issue
++ patch -p1 < path/to/MainMenuHooks-v1_0.patch
++and recompile.
++
++* Notes for plugin authors
++The following code sample shows the required plugin code for replacing
++the original Schedule menu:
++
++bool cMyPlugin::Service(const char *Id, void *Data)
++{
++ cOsdMenu **menu = (cOsdMenu**) Data;
++ if (MySetup.replaceSchedule &&
++ strcmp(Id, "MainMenuHooksPatch-v1.0::osSchedule") == 0) {
++ if (menu)
++ *menu = (cOsdMenu*) MainMenuAction();
++ return true;
++ }
++ return false;
++}
++
++A plugin can replace more than one menu at a time. Simply replace the
++call to MainMenuAction() in the sample above by appropriate code.
++
++Note that a plugin *should* offer a setup option which allows the user
++to enable or disable the replacement. "Disabled" would be a reasonable
++default setting. By testing for define MAINMENUHOOKSVERSNUM, a plugin
++can leave the setup option out at compiletime.
++
++In case there is an internal problem when trying to open the replacement
++menu, it is safe to return true even though Data is NULL. However an
++OSD message should indicate the problem to the user.
++
++Feel free to ship this patch along with your plugin. However if you
++think you need to modify the patch, we'd encourage you to contact the
++authors first or at least use a service id which differs in more than
++just the version number.
++
+diff -ruNp vdr-1.6.0-2/README.sortrec vdr-1.6.0-2-extensions/README.sortrec
+--- vdr-1.6.0-2/README.sortrec 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/README.sortrec 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,48 @@
++Sort Recordings patch for VDR
++-----------------------------
++Copyright (C) 2005 Frank99 @vdr-portal.de
++Copyright (C) 2006-2008 Christoph Haubrich
++
++Released under the same license as VDR itself, for details see vdr.c or
++http://firefly.vdr-developer.org/patches
++
++This patch changes the sort behaviour of the recordings menu. It is based
++on the patch available here: http://www.vdr-portal.de/board/thread.php?threadid=36031
++Required for this patch is the liemikuutio-patch for VDR which can be found here:
++http://www.saunalahti.fi/%7Erahrenbe/vdr/patches/
++
++There are four sorting modes available after this patch is applied:
++
++mode behaviour for behaviour for
++ main directory sub directories
++--------------------------------------------------------------------------
++ 0 alphabetically if special character(*) is found alphabetically,
++ else by date
++ 1 by date if special character(*) is found alphabetically,
++ else by date
++ 2 alphabetically alphabetically
++ 3 by date by date
++
++(*) if the name of a subdirectory ends with one of ".-$" (dot, hyphen, dollar sign)
++ it is sorted alphabetically in sort mode 0 and 1
++
++Sort mode 0 with none of the special characters at the end of any subdir
++corresponds to the default sorting mode of the original VDR.
++
++The sorting mode can be switched through in the recording menu with the '0' key
++(0->1->2->3->0->...), a default for startup can be set in the setup->recordings menu.
++
++Additionally the sort order (ascending/descending) can be toggled by the '9' key
++(which is always set to ascending after a restart)
++
++If you like the to see subdirectories before recordings you can select to put
++directories first in the setup->recordings menu.
++
++If you would like the sorting to ignore a leading '%' (as normally displayed before
++cutted recordings) you can achive this by setting the environment variable LC_COLLATE
++properly (eg. LC_COLLATE=de_DE@euo in runvdr for germany).
++
++History:
++2006-08-13 v3, sortrec release for VDR 1.4.1 and liemikuutio 1.8
++2007-01-28 v3a, moved #ifdef from optimized-rename-patch to sortrec
++2008-03-29 v3b, removed ASCII-170 and ASCII-183 to make sortrec Utf8-ready
+diff -ruNp vdr-1.6.0-2/README.timer-info vdr-1.6.0-2-extensions/README.timer-info
+--- vdr-1.6.0-2/README.timer-info 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/README.timer-info 2009-04-09 20:48:26.000000000 +0200
+@@ -0,0 +1,53 @@
+++------------------------------------------------------------------------------+
++| Info about the timer-info-patch by Brougs78 |
++| brougs78@gmx.net / home.pages.at/brougs78 |
+++------------------------------------------------------------------------------+
++
++
++README timer-info:
++------------------
++
++Features:
++ - Shows info, if it is possible to record an event in the timer menu of vdr.
++ For calculations the free space incl. the deleted recordings is used,
++ considering an average consumtion of 25.75 MB/min (also used by vdr itself).
++ The first column in the timer-list shows:
++ ( + ) recording will be most probably possible (enough space)
++ (+/-) recording may be possible
++ ( - ) recording will most probably fail (to less space)
++ The calculations also consider repeating timers.
++ - It is possible to deactivate the patch in the OSD-menu of VDR.
++
++
++HISTORY timer-info:
++-------------------
++
++25.11.2004: v0.1
++ - Initial release
++
++11.01.2005: v0.1b
++ - Bugfixes for vdr-1.3.18
++ - In the menu the free recording-time no longer includes the space of the
++ deleted recordings, because this slowed the vdr down to much.
++
++08.07.2005: v0.1c
++ - Made the patch configurable
++
++29.01.2006: v0.2 - Thomas Günther <tom@toms-cafe.de>
++ - Rewritten great parts for vdr-1.3.38+
++ http://toms-cafe.de/vdr/download/vdr-timer-info-0.2-1.3.38+.diff
++
++05.02.2006: v0.3 - Thomas Günther <tom@toms-cafe.de>
++ - Fixed refresh of timer menu in cMenuTimers::OnOff
++ - Fixed check of repeating timers
++ - Syslog debug messages can be enabled with Define DEBUG_TIMER_INFO
++ http://toms-cafe.de/vdr/download/vdr-timer-info-0.3-1.3.38+.diff
++
++03.03.2006: v0.4 - Thomas Günther <tom@toms-cafe.de>
++ - Adapted to vdr-1.3.44
++ - Removed setup parameter "Show timer-info"
++ http://toms-cafe.de/vdr/download/vdr-timer-info-0.4-1.3.44.diff
++
++03.03.2006: v0.5 - Tobias Grimm <tg@e-tobi.net>
++ - Adapted to vdr-1.3.45
++ http://toms-cafe.de/vdr/download/vdr-timer-info-0.4-1.3.45.diff
+diff -ruNp vdr-1.6.0-2/recorder.c vdr-1.6.0-2-extensions/recorder.c
+--- vdr-1.6.0-2/recorder.c 2007-02-24 17:36:24.000000000 +0100
++++ vdr-1.6.0-2-extensions/recorder.c 2009-04-09 20:48:27.000000000 +0200
+@@ -11,6 +11,9 @@
+ #include <stdarg.h>
+ #include <stdio.h>
+ #include <unistd.h>
++#ifdef USE_TTXTSUBS
++#include <stdint.h>
++#endif /* TTXTSUBS */
+ #include "shutdown.h"
+
+ #define RECORDERBUFSIZE MEGABYTE(5)
+@@ -26,6 +29,9 @@
+
+ class cFileWriter : public cThread {
+ private:
++#ifdef USE_TTXTSUBS
++ cTtxtSubsRecorderBase *ttxtSubsRecorder;
++#endif /* TTXTSUBS */
+ cRemux *remux;
+ cFileName *fileName;
+ cIndexFile *index;
+@@ -38,13 +44,24 @@ private:
+ protected:
+ virtual void Action(void);
+ public:
++#ifdef USE_TTXTSUBS
++ cFileWriter(const char *FileName, cRemux *Remux, cTtxtSubsRecorderBase *tsr);
++#else
+ cFileWriter(const char *FileName, cRemux *Remux);
++#endif /* TTXTSUBS */
+ virtual ~cFileWriter();
+ };
+
++#ifdef USE_TTXTSUBS
++cFileWriter::cFileWriter(const char *FileName, cRemux *Remux, cTtxtSubsRecorderBase *tsr)
++#else
+ cFileWriter::cFileWriter(const char *FileName, cRemux *Remux)
++#endif /* TTXTSUBS */
+ :cThread("file writer")
+ {
++#ifdef USE_TTXTSUBS
++ ttxtSubsRecorder = tsr;
++#endif /* TTXTSUBS */
+ fileName = NULL;
+ remux = Remux;
+ index = NULL;
+@@ -67,6 +84,10 @@ cFileWriter::~cFileWriter()
+ Cancel(3);
+ delete index;
+ delete fileName;
++#ifdef USE_TTXTSUBS
++ if (ttxtSubsRecorder)
++ delete ttxtSubsRecorder;
++#endif /* TTXTSUBS */
+ }
+
+ bool cFileWriter::RunningLowOnDiskSpace(void)
+@@ -85,7 +106,11 @@ bool cFileWriter::RunningLowOnDiskSpace(
+ bool cFileWriter::NextFile(void)
+ {
+ if (recordFile && pictureType == I_FRAME) { // every file shall start with an I_FRAME
++#ifndef USE_HARDLINKCUTTER
+ if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
++#else
++ if (fileSize > fileName->MaxFileSize() || RunningLowOnDiskSpace()) {
++#endif /* HARDLINKCUTTER */
+ recordFile = fileName->NextFile();
+ fileSize = 0;
+ }
+@@ -111,6 +136,18 @@ void cFileWriter::Action(void)
+ }
+ fileSize += Count;
+ remux->Del(Count);
++#ifdef USE_TTXTSUBS
++ // not sure if the pictureType test is needed, but it seems we can get
++ // incomplete pes packets from remux if we are not getting pictures?
++ if (ttxtSubsRecorder && pictureType != NO_PICTURE) {
++ uint8_t *subsp;
++ size_t len;
++ if (ttxtSubsRecorder->GetPacket(&subsp, &len)) {
++ recordFile->Write(subsp, len);
++ fileSize += len;
++ }
++ }
++#endif /* TTXTSUBS */
+ }
+ else
+ break;
+@@ -126,8 +163,23 @@ void cFileWriter::Action(void)
+
+ // --- cRecorder -------------------------------------------------------------
+
++#if defined (USE_LIVEBUFFER) || defined (USE_TTXTSUBS)
++cRecorder::cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids
++#ifdef USE_TTXTSUBS
++ ,cTtxtSubsRecorderBase *tsr
++#endif /* TTXTSUBS */
++#ifdef USE_LIVEBUFFER
++ ,cLiveBuffer *LiveBuffer
++#endif /* LIVEBUFFER */
++ )
++#else
+ cRecorder::cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
++#endif /* LIVEBUFFER || TTXTSUBS */
++#ifdef USE_DOLBYINREC
++:cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyInRecordings ? DPids : NULL, SPids)
++#else
+ :cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
++#endif /* DOLBYINREC */
+ ,cThread("recording")
+ {
+ // Make sure the disk is up and running:
+@@ -136,8 +188,22 @@ cRecorder::cRecorder(const char *FileNam
+
+ ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, TS_SIZE * 2, true, "Recorder");
+ ringBuffer->SetTimeouts(0, 100);
++#ifdef USE_DOLBYINREC
++ remux = new cRemux(VPid, APids, Setup.UseDolbyInRecordings ? DPids : NULL, SPids, true);
++#else
+ remux = new cRemux(VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids, true);
++#endif /* DOLBYINREC */
++#ifdef USE_LIVEBUFFER
++ fileName = strdup(FileName);
++ writer = NULL;
++ liveBuffer = LiveBuffer;
++ if (!LiveBuffer)
++#endif /* LIVEBUFFER */
++#ifdef USE_TTXTSUBS
++ writer = new cFileWriter(FileName, remux, tsr);
++#else
+ writer = new cFileWriter(FileName, remux);
++#endif /* TTXTSUBS */
+ }
+
+ cRecorder::~cRecorder()
+@@ -146,11 +212,17 @@ cRecorder::~cRecorder()
+ delete writer;
+ delete remux;
+ delete ringBuffer;
++#ifdef USE_LIVEBUFFER
++ free(fileName);
++#endif /* LIVEBUFFER */
+ }
+
+ void cRecorder::Activate(bool On)
+ {
+ if (On) {
++#ifdef USE_LIVEBUFFER
++ if (writer)
++#endif /* LIVEBUFFER */
+ writer->Start();
+ Start();
+ }
+@@ -174,6 +246,29 @@ void cRecorder::Action(void)
+ uchar *b = ringBuffer->Get(r);
+ if (b) {
+ int Count = remux->Put(b, r);
++#ifdef USE_LIVEBUFFER
++ if (!writer && liveBuffer) {
++ int c;
++ uchar pictureType;
++ uchar *p = remux->Get(c, &pictureType);
++ if (pictureType == I_FRAME && p && p[0]==0x00 && p[1]==0x00 && p[2]==0x01 && (p[7] & 0x80) && p[3]>=0xC0 && p[3]<=0xEF) {
++ int64_t pts = (int64_t) (p[ 9] & 0x0E) << 29 ;
++ pts |= (int64_t) p[ 10] << 22 ;
++ pts |= (int64_t) (p[ 11] & 0xFE) << 14 ;
++ pts |= (int64_t) p[ 12] << 7 ;
++ pts |= (int64_t) (p[ 13] & 0xFE) >> 1 ;
++ liveBuffer->CreateIndexFile(fileName, pts);
++#ifdef USE_TTXTSUBS
++ writer = new cFileWriter(fileName, remux, ttxtSubsRecorder);
++#else
++ writer = new cFileWriter(fileName, remux);
++#endif /* TTXTSUBS */
++ writer->Start();
++ }
++ else
++ remux->Del(c);
++ }
++#endif /* LIVEBUFFER */
+ if (Count)
+ ringBuffer->Del(Count);
+ else
+diff -ruNp vdr-1.6.0-2/recorder.h vdr-1.6.0-2-extensions/recorder.h
+--- vdr-1.6.0-2/recorder.h 2007-01-05 11:44:05.000000000 +0100
++++ vdr-1.6.0-2-extensions/recorder.h 2009-04-09 20:48:27.000000000 +0200
+@@ -10,11 +10,17 @@
+ #ifndef __RECORDER_H
+ #define __RECORDER_H
+
++#ifdef USE_LIVEBUFFER
++#include "livebuffer.h"
++#endif /* LIVEBUFFER */
+ #include "receiver.h"
+ #include "recording.h"
+ #include "remux.h"
+ #include "ringbuffer.h"
+ #include "thread.h"
++#ifdef USE_TTXTSUBS
++#include "vdrttxtsubshooks.h"
++#endif /* TTXTSUBS */
+
+ class cFileWriter;
+
+@@ -23,12 +29,30 @@ private:
+ cRingBufferLinear *ringBuffer;
+ cRemux *remux;
+ cFileWriter *writer;
++#ifdef USE_LIVEBUFFER
++ char *fileName;
++ cLiveBuffer *liveBuffer;
++#ifdef USE_TTXTSUBS
++ cTtxtSubsRecorderBase *ttxtSubsRecorder;
++#endif /* TTXTSUBS */
++#endif /* LIVEBUFFER */
+ protected:
+ virtual void Activate(bool On);
+ virtual void Receive(uchar *Data, int Length);
+ virtual void Action(void);
+ public:
++#if defined (USE_LIVEBUFFER) || defined (USE_TTXTSUBS)
++ cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids
++#ifdef USE_TTXTSUBS
++ ,cTtxtSubsRecorderBase *tsr
++#endif /* TTXTSUBS */
++#ifdef USE_LIVEBUFFER
++ ,cLiveBuffer *LiveBuffer = NULL
++#endif /* LIVEBUFFER */
++ );
++#else
+ cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids);
++#endif /* LIVEBUFFER || TTXTSUBS */
+ // Creates a new recorder for the channel with the given ChannelID and
+ // the given Priority that will record the given PIDs into the file FileName.
+ virtual ~cRecorder();
+diff -ruNp vdr-1.6.0-2/recording.c vdr-1.6.0-2-extensions/recording.c
+--- vdr-1.6.0-2/recording.c 2008-02-24 11:28:53.000000000 +0100
++++ vdr-1.6.0-2-extensions/recording.c 2009-04-09 20:48:27.000000000 +0200
+@@ -8,6 +8,9 @@
+ */
+
+ #include "recording.h"
++#ifdef USE_WAREAGLEICON
++#include "iconpatch.h"
++#endif /* WAREAGLEICON */
+ #include <ctype.h>
+ #include <dirent.h>
+ #include <errno.h>
+@@ -23,6 +26,17 @@
+ #include "skins.h"
+ #include "tools.h"
+ #include "videodir.h"
++#ifdef USE_STREAMDEVEXT
++#include "status.h"
++#endif /* STREAMDEVEXT */
++
++#if defined (USE_DVDCHAPJUMP) && defined (USE_DVDARCHIVE)
++#include <assert.h>
++/* libdvdread stuff */
++#include <dvdread/dvd_reader.h>
++#include <dvdread/ifo_types.h>
++#include <dvdread/ifo_read.h>
++#endif /* DVDCHAPJUMP & DVDARCHIVE */
+
+ #define SUMMARYFALLBACK
+
+@@ -46,6 +60,12 @@
+ #endif
+ #define INFOFILESUFFIX "/info.vdr"
+ #define MARKSFILESUFFIX "/marks.vdr"
++#ifdef USE_LIEMIEXT
++#define INDEXFILESUFFIX "/index.vdr"
++#endif /* LIEMIEXT */
++#ifdef USE_DVDARCHIVE
++#define DVDARCHIVEFILENAME "/dvd.vdr"
++#endif /* DVDARCHIVE */
+
+ #define MINDISKSPACE 1024 // MB
+
+@@ -62,6 +82,13 @@
+ #define MAX_LINK_LEVEL 6
+
+ bool VfatFileSystem = false;
++#ifdef USE_LIEMIEXT
++bool DirOrderState = false;
++#endif /* LIEMIEXT */
++
++#ifdef USE_DVLFRIENDLYFNAMES
++char *MakeFriendlyFilename(char **buf);
++#endif /* DVLFRIENDLYFNAMES */
+
+ cRecordings DeletedRecordings(true);
+
+@@ -491,9 +518,24 @@ cRecording::cRecording(cTimer *Timer, co
+ {
+ resume = RESUME_NOT_INITIALIZED;
+ titleBuffer = NULL;
++#ifdef USE_SORTRECORDS
++ for (int i = 0; i < MAXSORTMODES; i++) {
++ sortBuffer[i] = NULL;
++ lastDirsFirst[i] = -1;
++ }
++#else
+ sortBuffer = NULL;
++#endif /* SORTRECORDS */
+ fileName = NULL;
+ name = NULL;
++#ifdef USE_DVDARCHIVE
++ dvdname = NULL;
++ dvdtrack = NULL;
++ dvdchapters = NULL;
++ isArchived = false;
++ isOnlyOnDvd = false;
++ dvdtype = DVD_TYPE_UNKNOWN;
++#endif /* DVDARCHIVE */
+ fileSizeMB = -1; // unknown
+ deleted = 0;
+ // set up the actual name:
+@@ -524,6 +566,11 @@ cRecording::cRecording(cTimer *Timer, co
+ break;
+ }
+ if (Timer->IsSingleEvent()) {
++#ifdef USE_DVLFRIENDLYFNAMES
++ if (Setup.UseFriendlyFNames == 1)
++ Timer -> SetFile(MakeFriendlyFilename(&name));
++ else
++#endif /* DVLFRIENDLYFNAMES */
+ Timer->SetFile(name); // this was an instant recording, so let's set the actual data
+ Timers.SetModified();
+ }
+@@ -534,6 +581,10 @@ cRecording::cRecording(cTimer *Timer, co
+ name = strdup(cString::sprintf("%s~%s", Timer->File(), Subtitle));
+ // substitute characters that would cause problems in file names:
+ strreplace(name, '\n', ' ');
++#ifdef USE_DVLFRIENDLYFNAMES
++ if (Setup.UseFriendlyFNames == 1)
++ MakeFriendlyFilename(&name);
++#endif /* DVLFRIENDLYFNAMES */
+ start = Timer->StartTime();
+ priority = Timer->Priority();
+ lifetime = Timer->Lifetime();
+@@ -548,12 +599,27 @@ cRecording::cRecording(const char *FileN
+ fileSizeMB = -1; // unknown
+ deleted = 0;
+ titleBuffer = NULL;
++#ifdef USE_SORTRECORDS
++ for (int i = 0; i < MAXSORTMODES; i++) {
++ sortBuffer[i] = NULL;
++ lastDirsFirst[i] = -1;
++ }
++#else
+ sortBuffer = NULL;
++#endif /* SORTRECORDS */
+ fileName = strdup(FileName);
+ FileName += strlen(VideoDirectory) + 1;
+ char *p = strrchr(FileName, '/');
+
+ name = NULL;
++#ifdef USE_DVDARCHIVE
++ dvdname = NULL;
++ dvdtrack = NULL;
++ dvdchapters = NULL;
++ isArchived = false;
++ isOnlyOnDvd = false;
++ dvdtype = DVD_TYPE_NOT_READ;
++#endif /* DVDARCHIVE */
+ info = new cRecordingInfo;
+ if (p) {
+ time_t now = time(NULL);
+@@ -633,15 +699,34 @@ cRecording::cRecording(const char *FileN
+ LOG_ERROR_STR(*SummaryFileName);
+ }
+ #endif
++#ifdef USE_DVDARCHIVE
++ if (CheckFileExistence("dvd.vdr")) {
++ GetDvdName(fileName);
++ isArchived = true;
++ if (!CheckFileExistence("001.vdr"))
++ isOnlyOnDvd = true;
++ }
++#endif /* DVDARCHIVE */
+ }
+ }
+
+ cRecording::~cRecording()
+ {
+ free(titleBuffer);
++#ifdef USE_SORTRECORDS
++ for (int i = 0; i < MAXSORTMODES; i++) {
++ free(sortBuffer[i]);
++ }
++#else
+ free(sortBuffer);
++#endif /* SORTRECORDS */
+ free(fileName);
+ free(name);
++#ifdef USE_DVDARCHIVE
++ free(dvdname);
++ free(dvdtrack);
++ free(dvdchapters);
++#endif /* DVDARCHIVE */
+ delete info;
+ }
+
+@@ -661,21 +746,46 @@ char *cRecording::StripEpisodeName(char
+ t++;
+ }
+ if (s1 && s2)
++#ifdef USE_SORTRECORDS
++ if (Setup.RecordingsSortDirsFirst)
++ *s1 = 'b';
++
++ if ((Setup.RecordingsSortMode <= 1 && s1 != s && !strchr(".-$ª·", *(s1 - 1))) ||
++ (Setup.RecordingsSortMode == 1 && s1 == s) ||
++ (Setup.RecordingsSortMode == 3))
++#endif /* SORTRECORDS */
+ memmove(s1 + 1, s2, t - s2 + 1);
+ return s;
+ }
+
+ char *cRecording::SortName(void) const
+ {
++#ifdef USE_SORTRECORDS
++ if (!sortBuffer[Setup.RecordingsSortMode] ||
++ lastDirsFirst[Setup.RecordingsSortMode] != Setup.RecordingsSortDirsFirst) {
++ free(sortBuffer[Setup.RecordingsSortMode]);
++ lastDirsFirst[Setup.RecordingsSortMode] = Setup.RecordingsSortDirsFirst;
++ char *s = StripEpisodeName(strdup(FileName() + strlen(VideoDirectory)));
++#else
+ if (!sortBuffer) {
+ char *s = StripEpisodeName(strdup(FileName() + strlen(VideoDirectory) + 1));
++#endif /* SORTRECORDS */
+ strreplace(s, '/', 'a'); // some locales ignore '/' when sorting
+ int l = strxfrm(NULL, s, 0) + 1;
++#ifdef USE_SORTRECORDS
++ sortBuffer[Setup.RecordingsSortMode] = MALLOC(char, l);
++ strxfrm(sortBuffer[Setup.RecordingsSortMode], s, l);
++#else
+ sortBuffer = MALLOC(char, l);
+ strxfrm(sortBuffer, s, l);
++#endif /* SORTRECORDS */
+ free(s);
+ }
++#ifdef USE_SORTRECORDS
++ return sortBuffer[Setup.RecordingsSortMode];
++#else
+ return sortBuffer;
++#endif /* SORTRECORDS */
+ }
+
+ int cRecording::GetResume(void) const
+@@ -690,7 +800,15 @@ int cRecording::GetResume(void) const
+ int cRecording::Compare(const cListObject &ListObject) const
+ {
+ cRecording *r = (cRecording *)&ListObject;
++#ifdef USE_SORTRECORDS
++ return Recordings.GetSortOrder() * strcasecmp(SortName(), r->SortName());
++#else
++#ifdef USE_LIEMIEXT
++ if (DirOrderState)
++ return strcasecmp(FileName(), r->FileName());
++#endif /* LIEMIEXT */
+ return strcasecmp(SortName(), r->SortName());
++#endif /* USE_SORTRECORDS */
+ }
+
+ const char *cRecording::FileName(void) const
+@@ -705,9 +823,359 @@ const char *cRecording::FileName(void) c
+ return fileName;
+ }
+
++#ifdef USE_DVDARCHIVE
++bool cRecording::CheckFileExistence(const char* FileNameToTest, const bool useVideoDir) const
++{
++ if (!useVideoDir || (useVideoDir && FileName())) {
++ cString filename = cString::sprintf("%s%s%s", useVideoDir ? FileName() : "",
++ useVideoDir ? "/" : "",
++ FileNameToTest);
++ struct stat statBuf;
++ if (lstat(filename, &statBuf) == -1) return false;
++ return S_ISREG(statBuf.st_mode) || S_ISLNK(statBuf.st_mode);
++ }
++ return false;
++}
++
++bool cRecording::GetDvdName(const char* Directory) const
++{
++ char* filename = (char*)alloca(strlen(Directory) + strlen(DVDARCHIVEFILENAME) + 1);
++ if (filename) {
++ strcpy(filename, Directory);
++ char *end = filename + strlen(filename);
++ strcpy(end, DVDARCHIVEFILENAME);
++ FILE* file;
++ if ((file = fopen(filename, "r"))) {
++ cReadLine ReadLine;
++ char* buffer = (char*)alloca(BUFSIZ);
++ if (buffer) {
++ buffer = ReadLine.Read(file);
++ if (buffer)
++ ((cRecording*)this)->dvdname = strdup(buffer);
++ else
++ ((cRecording*)this)->dvdname = NULL;
++
++ buffer = ReadLine.Read(file);
++ if (buffer) {
++ ((cRecording*)this)->dvdtrack = strdup(buffer);
++ if (atoi(buffer) == 0)
++ ((cRecording*)this)->dvdtype = DVD_VIDEO_TYPE;
++ else
++ ((cRecording*)this)->dvdtype = DVD_VIDEO_ARCHIVE_TYPE;
++ }
++ else {
++ ((cRecording*)this)->dvdtrack = NULL;
++ ((cRecording*)this)->dvdtype = DVD_ARCHIVE_TYPE;
++ }
++
++ fclose(file);
++ return true;
++ }
++ }
++ }
++ return false;
++}
++
++bool cRecording::GetDvdChaptersFromDvd(int title) const
++{
++#ifdef USE_DVDCHAPJUMP
++ cString buf;
++
++ dvd_reader_t *dvd;
++ ifo_handle_t *ifo_file;
++ tt_srpt_t *tt_srpt;
++ ifo_handle_t *vts_file;
++ pgc_t *cur_pgc;
++
++ dvd = DVDOpen(DVD_DEVICE);
++ if (!dvd) {
++ esyslog("DVD-ARCHIVE: Couldn't open DVD device %s!", DVD_DEVICE);
++ return false;
++ }
++
++ /* open title manager */
++ ifo_file = ifoOpen(dvd,0);
++ if (!ifo_file) {
++ esyslog("DVD-ARCHIVE: Can't open VMG info.");
++ DVDClose(dvd);
++ return false;
++ }
++
++ /* read total_title */
++ tt_srpt = ifo_file->tt_srpt;
++
++ /* get total chapters */
++ int title_set_nr = tt_srpt->title[title-1].title_set_nr;
++ int total_chap = tt_srpt->title[title-1].nr_of_ptts;
++ int local_title_id = tt_srpt->title[title-1].vts_ttn - 1;
++
++ /* access title set file */
++ vts_file = ifoOpen(dvd, title_set_nr);
++ if (!vts_file) {
++ esyslog("DVD-ARCHIVE: Can't open info file for title set %d!",title_set_nr);
++ DVDClose(dvd);
++ return false;
++ }
++
++ /* find program chain and check programs
++ all chapters should be in the same prog chain and
++ should be numbered from 1 to <total_chap>
++ */
++ {
++ vts_ptt_srpt_t *vts_ptt_srpt = vts_file->vts_ptt_srpt;
++ int pgc_nr = vts_ptt_srpt->title[local_title_id].ptt[0].pgcn;
++ int pg = vts_ptt_srpt->title[local_title_id].ptt[0].pgn;
++ int p;
++
++ assert(pg==1);
++ for (p=1; p<total_chap; p++) {
++ int next_pg;
++ int this_pgc;
++ this_pgc = vts_ptt_srpt->title[local_title_id].ptt[p].pgcn;
++ assert(pgc_nr == this_pgc);
++ next_pg = vts_ptt_srpt->title[local_title_id].ptt[p].pgn;
++ assert(pg+1 == next_pg);
++ pg = next_pg;
++ }
++
++ /* fetch program chain */
++ cur_pgc = vts_file->vts_pgcit->pgci_srp[pgc_nr-1].pgc;
++ assert(cur_pgc->nr_of_programs == total_chap);
++ }
++
++ /* --- main cell loop --- */
++ {
++ pgc_program_map_t *chap_cell;
++ cell_playback_t *cell_pb;
++ int c;
++ int chap;
++
++ /* total cells in chain */
++ int total_cell = cur_pgc->nr_of_cells;
++
++ /* get info */
++ chap_cell = cur_pgc->program_map;
++ cell_pb = cur_pgc->cell_playback;
++
++ /* loop through all cells */
++ chap = -1;
++ int position = 0;
++ for (c=0; c<total_cell; c++) {
++ int cell_mode;
++ const char *mode;
++
++ dvd_time_t *time = (dvd_time_t *)&cell_pb->playback_time;
++
++ int framerate = time->frame_u>>6;
++ assert(framerate == 1 || framerate == 3);
++ int frames_per_sec = (framerate == 1) ? 25 : 30;
++
++ /* upper 4 bits are first digit, down 4 bits are second digit */
++ int hour = (time->hour>>4) * 10 + (time->hour&15);
++ int minute = (time->minute>>4) * 10 + (time->minute&15);
++ int second = (time->second>>4) * 10 + (time->second&15);
++ /* upper 4 bits are first digit, down 4 bits are second digit */
++ int frame = (time->frame_u>>4&3) * 10 + (time->frame_u&15);
++
++ int frames = ((hour * 3600) + (minute * 60) + second) * frames_per_sec + frame;
++
++ /* this cell is the begin of a new chapter! */
++ if (chap_cell[chap+1] == c+1) {
++ cString oldbuf = cString::sprintf("%s", *buf);
++ buf = cString::sprintf("%s%d%s", *oldbuf, position, ((chap+2) < total_chap) ? "," : "");
++ chap++;
++ }
++
++ /* cell_mode: 0=normal, 1=first of angle, 2=in angle, 3=last of angle */
++ cell_mode = cell_pb->block_mode;
++ if ((cell_mode==0) || (cell_mode==1)) {
++ /* only account for normal or begin of angle cells */
++ position += frames;
++ mode = "counted";
++ }
++ else
++ mode = "skipped";
++
++ cell_pb++;
++ }
++ }
++
++ ifoClose(ifo_file);
++ ifoClose(vts_file);
++ DVDClose(dvd);
++
++ ((cRecording*)this)->dvdchapters = strdup(buf);
++
++ return true;
++#else
++ return false;
++#endif /* DVDCHAPJUMP */
++}
++
++const char *cRecording::GetDvdChapters(void) const
++{
++ // Read chapters from dvd
++ if (dvdtype == DVD_VIDEO_ARCHIVE_TYPE) {
++ if (dvdtrack) {
++ if (!GetDvdChaptersFromDvd(atoi(dvdtrack)))
++ ((cRecording*)this)->dvdchapters = NULL;
++ else
++ isyslog("DVD-ARCHIVE: Using following positions for chapter jumping: %s", dvdchapters);
++ return dvdchapters;
++ }
++ }
++ return NULL;
++}
++
++int cRecording::MountDvd(void) const
++{
++ cString cmd;
++ if (Setup.DvdSpeedLimit > 0) {
++ cmd = cString::sprintf("speedcontrol -x %d %s", Setup.DvdSpeedLimit, DVD_DEVICE);
++ SystemExec(cmd);
++ }
++
++ cString msg;
++ if (atoi(dvdname) == 0)
++ msg = cString::sprintf(tr("Please mount %s"), dvdname);
++ else {
++ if (Setup.DvdDisplayZeros)
++ msg = cString::sprintf(tr("Please mount DVD %04d"), atoi(dvdname));
++ else
++ msg = cString::sprintf(tr("Please mount DVD %d"), atoi(dvdname));
++ }
++
++ bool rep = true;
++ while (rep) {
++ if (Setup.DvdTrayMode==1 || Setup.DvdTrayMode==3)
++ cmd = cString::sprintf("umount %s; eject %s", DVD_DEVICE, DVD_DEVICE);
++ else
++ cmd = cString::sprintf("umount %s", DVD_DEVICE);
++ SystemExec(cmd);
++
++ if (Interface->Confirm(msg, 300)) {
++ Skins.Message(mtStatus, tr("Please wait. Checking DVD..."));
++ Skins.Flush();
++ cmd = cString::sprintf("eject -t %s; mkdir -p %s; mount -o ro -t %s %s %s",
++ DVD_DEVICE, DVD_MOUNT_PATH,
++ (dvdtrack ? "udf" : "iso9660"),
++ DVD_DEVICE, DVD_MOUNT_PATH);
++ SystemExec(cmd);
++
++ bool correctDvd = true;
++
++ char *olddvdname, *olddvdtrack;
++ int olddvdtype;
++ olddvdname = dvdname;
++ olddvdtrack = dvdtrack;
++ olddvdtype = dvdtype;
++ if (GetDvdName(DVD_MOUNT_PATH)) {
++ if (atoi(dvdname) != atoi(olddvdname)) correctDvd = false;
++ }
++ ((cRecording*)this)->dvdname = olddvdname;
++ ((cRecording*)this)->dvdtrack = olddvdtrack;
++ ((cRecording*)this)->dvdtype = olddvdtype;
++
++ if (correctDvd) {
++ if (dvdtrack == NULL) {
++ // Archived DVD in VDR format
++ char fn[BUFSIZ];
++ strcpy(fn, FileName());
++ char *p = strrchr(fn, '/');
++ cmd = cString::sprintf("find '%s' -name '%s'", DVD_MOUNT_PATH, p+1);
++ }
++ else {
++ // Either archived DVD in DVD-Video format or DVD-Video which
++ // should be played with the DVD plugin
++ cmd = cString::sprintf("find '%s' -iname 'VIDEO_TS'", DVD_MOUNT_PATH);
++ }
++
++ cReadLine pipe;
++ FILE* file;
++ char *dirname = NULL;
++ if ((file = popen(cmd, "r")) != (FILE *)NULL) {
++ if ((dirname = pipe.Read(file)) != NULL) {
++ pclose(file);
++ if (dvdtrack != NULL && atoi(dvdtrack) == 0) {
++ // It is a valid Video-DVD and DVD plugin can be started
++ return MOUNT_DVD_LAUNCH_DVD_PLUGIN;
++ }
++ else {
++ // It is a valid Archive-DVD or an archived Video-DVD
++ // and the links can now be established
++ cString srcFn;
++ int n = 1;
++
++ do {
++ if (dvdtrack == NULL)
++ srcFn = cString::sprintf("%s/%03d.vdr", dirname, n);
++ else
++ srcFn = cString::sprintf("%s/VTS_%02d_%d.VOB", dirname, atoi(dvdtrack), n);
++
++ if (!access(srcFn, R_OK)) {
++ cmd = cString::sprintf("ln -sf '%s' '%s/%03d.vdr'", *srcFn, FileName(), n);
++ SystemExec(cmd);
++ isyslog("DVD-ARCHIVE: Linking %s/%03d.vdr -> %s", FileName(), n, *srcFn);
++ }
++ else
++ break;
++ } while ( ++n < 999);
++
++ if (!CheckFileExistence("index.vdr")) {
++ if (dvdtrack == NULL)
++ srcFn = cString::sprintf("%s/index.vdr", dirname);
++ else
++ srcFn = cString::sprintf("%s/index_%02d.vdr", dirname, atoi(dvdtrack));
++
++ if (!CheckFileExistence(srcFn, false)) {
++ msg = cString::sprintf(tr("No index-file found. Creating may take minutes. Create one?"));
++ if (Interface->Confirm(msg, 300)) {
++ Skins.Message(mtStatus, tr("Please wait. Creating index-file..."));
++ cmd = cString::sprintf("speedcontrol -x 999 %s; cd %s && genindex &", DVD_DEVICE, FileName());
++ SystemExec(cmd);
++ return MOUNT_DVD_ABORT;
++ }
++ }
++ else {
++ cmd = cString::sprintf("ln -sf '%s' '%s/index.vdr'", *srcFn, FileName());
++ SystemExec(cmd);
++ isyslog("DVD-ARCHIVE: Linking %s/index.vdr -> %s", FileName(), *srcFn);
++ }
++ }
++ return MOUNT_DVD_REPLAY;
++ }
++ }
++ else {
++ Skins.Message(mtError, tr("Wrong DVD!"), 3);
++ Skins.Flush();
++ }
++ }
++ pclose(file);
++ }
++ else {
++ Skins.Message(mtError, tr("Wrong DVD!"), 3);
++ Skins.Flush();
++ }
++ }
++ else {
++ rep = false;
++ }
++ }
++ return MOUNT_DVD_ABORT;
++}
++
++#endif /* DVDARCHIVE */
++#ifdef USE_LIEMIEXT
++const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level, bool Original) const
++#else
+ const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level) const
++#endif /* LIEMIEXT */
+ {
++#ifdef USE_WAREAGLEICON
++ const char *New = NewIndicator && IsNew() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_NEW_UTF8 : ICON_NEW : "*" : " ";
++#else
+ char New = NewIndicator && IsNew() ? '*' : ' ';
++#endif /* WAREAGLEICON */
+ free(titleBuffer);
+ titleBuffer = NULL;
+ if (Level < 0 || Level == HierarchyLevels()) {
+@@ -718,7 +1186,14 @@ const char *cRecording::Title(char Delim
+ s++;
+ else
+ s = name;
++#ifdef USE_LIEMIEXT
++ if (Original) {
++#endif /* LIEMIEXT */
++#ifdef USE_WAREAGLEICON
++ titleBuffer = strdup(cString::sprintf("%02d.%02d.%02d%c%02d:%02d%s%c%s",
++#else
+ titleBuffer = strdup(cString::sprintf("%02d.%02d.%02d%c%02d:%02d%c%c%s",
++#endif /* WAREAGLEICON */
+ t->tm_mday,
+ t->tm_mon + 1,
+ t->tm_year % 100,
+@@ -728,6 +1203,92 @@ const char *cRecording::Title(char Delim
+ New,
+ Delimiter,
+ s));
++#ifdef USE_LIEMIEXT
++ }
++ else {
++ cString RecLength("---");
++ if (Setup.ShowRecLength && FileName()) {
++ cString filename = cString::sprintf("%s%s", FileName(), INDEXFILESUFFIX);
++ if (*filename) {
++ if (access(filename, R_OK) == 0) {
++ struct stat buf;
++ if (stat(filename, &buf) == 0) {
++ struct tIndex { int offset; uchar type; uchar number; short reserved; };
++ int delta = buf.st_size % sizeof(tIndex);
++ if (delta) {
++ delta = sizeof(tIndex) - delta;
++ esyslog("ERROR: invalid file size (%ld) in '%s'", buf.st_size, *filename);
++ }
++ RecLength = cString::sprintf("%ld'", (buf.st_size + delta) / sizeof(tIndex) / SecondsToFrames(60));
++ }
++ }
++ }
++ }
++#endif /* LIEMIEXT */
++#ifdef USE_DVDARCHIVE
++#ifdef USE_WAREAGLEICON
++ if (isArchived && !isOnlyOnDvd) New = Setup.WarEagleIcons ? IsLangUtf8() ? ICON_DVD_UTF8 : ICON_DVD : "~";
++#else
++ if (isArchived && !isOnlyOnDvd) New = '~';
++#endif /* WAREAGLEICON */
++
++ if (isOnlyOnDvd && Setup.DvdDisplayMode >= 1) {
++ char oldLength[21];
++
++ if (strrchr(RecLength, '\''))
++ sprintf(oldLength,"%s", *RecLength);
++ else
++ oldLength[0] = 0;
++
++ if (dvdname) {
++ if (atoi(dvdname) != 0) {
++ cString tmp;
++ if (Setup.DvdDisplayZeros)
++ tmp = cString::sprintf("%04d", atoi(dvdname));
++ else {
++ int num = atoi(dvdname);
++ bool displaySpace = !(Setup.DvdDisplayMode == 1 && oldLength[0] != 0);
++ // ugly hack to have 2 spaces instead of one 0 for each place
++ tmp = cString::sprintf("%s%s%s%d", displaySpace && (num < 1000) ? " " : "",
++ displaySpace && (num < 100) ? " " : "",
++ displaySpace && (num < 10) ? " " : "",
++ num);
++ }
++ ((cRecording*)this)->dvdname = strdup(tmp);
++ }
++ }
++
++ RecLength = strdup(cString::sprintf("%s%s%s%s", (Setup.ShowRecLength && (Setup.DvdDisplayMode == 1) && (oldLength[0] != 0)) ? oldLength : "",
++ (dvdname && isArchived && isOnlyOnDvd && Setup.ShowRecLength && (Setup.DvdDisplayMode == 1) && (oldLength[0] != 0)) ? " / " : "",
++ (dvdname && isArchived && isOnlyOnDvd) ? dvdname : "",
++ (dvdname && isArchived && isOnlyOnDvd) ? " " : ""
++ ));
++ }
++#endif /* DVDARCHIVE */
++#ifdef USE_LIEMIEXT
++ cString RecDate = cString::sprintf("%02d.%02d.%02d", t->tm_mday, t->tm_mon + 1, t->tm_year % 100);
++ cString RecTime = cString::sprintf("%02d:%02d", t->tm_hour, t->tm_min);
++ cString RecDelimiter = cString::sprintf("%c", Delimiter);
++#ifdef USE_WAREAGLEICON
++ titleBuffer = strdup(cString::sprintf("%s%s%s%s%s%s%s%s",
++#else
++ titleBuffer = strdup(cString::sprintf("%s%s%s%c%s%s%s%s",
++#endif /* WAREAGLEICON */
++ (Setup.ShowRecDate ? *RecDate : ""),
++ (Setup.ShowRecDate && Setup.ShowRecTime ? *RecDelimiter : ""),
++ (Setup.ShowRecTime ? *RecTime : ""),
++ New,
++ (Setup.ShowRecTime || Setup.ShowRecDate ? *RecDelimiter : ""),
++#ifdef USE_DVDARCHIVE
++ (((Setup.ShowRecLength + Setup.DvdDisplayMode) > 0) ? *RecLength : ""),
++ (((Setup.ShowRecLength + Setup.DvdDisplayMode) > 0) ? *RecDelimiter : ""),
++#else
++ (Setup.ShowRecLength ? *RecLength : ""),
++ (Setup.ShowRecLength ? *RecDelimiter : ""),
++#endif /* DVDARCHIVE */
++ s));
++ }
++#endif /* LIEMIEXT */
+ // let's not display a trailing '~':
+ if (!NewIndicator)
+ stripspace(titleBuffer);
+@@ -756,6 +1317,17 @@ const char *cRecording::Title(char Delim
+ return titleBuffer;
+ }
+
++#ifdef USE_CUTTIME
++void cRecording::SetStartTime(time_t Start)
++{
++ start = Start;
++ if (fileName) {
++ free(fileName);
++ fileName = NULL;
++ }
++}
++#endif /* CUTTIME */
++
+ const char *cRecording::PrefixFileName(char Prefix)
+ {
+ cString p = PrefixVideoFileName(FileName(), Prefix);
+@@ -809,10 +1381,20 @@ bool cRecording::Delete(void)
+ // the new name already exists, so let's remove that one first:
+ isyslog("removing recording '%s'", NewName);
+ RemoveVideoFile(NewName);
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgRecordingChange(this, scDel);
++#endif /* STREAMDEVEXT */
+ }
+ isyslog("deleting recording '%s'", FileName());
++#ifdef USE_STREAMDEVEXT
++ if (access(FileName(), F_OK) == 0) {
++ result = RenameVideoFile(FileName(), NewName);
++ cStatus::MsgRecordingChange(this, scDel);
++ }
++#else
+ if (access(FileName(), F_OK) == 0)
+ result = RenameVideoFile(FileName(), NewName);
++#endif /* STREAMDEVEXT */
+ else {
+ isyslog("recording '%s' vanished", FileName());
+ result = true; // well, we were going to delete it, anyway
+@@ -830,7 +1412,13 @@ bool cRecording::Remove(void)
+ return false;
+ }
+ isyslog("removing recording %s", FileName());
++#ifdef USE_STREAMDEVEXT
++ bool ret = RemoveVideoFile(FileName());
++ cStatus::MsgRecordingChange(this, scDel);
++ return ret;
++#else
+ return RemoveVideoFile(FileName());
++#endif /* STREAMDEVEXT */
+ }
+
+ bool cRecording::Undelete(void)
+@@ -847,8 +1435,15 @@ bool cRecording::Undelete(void)
+ }
+ else {
+ isyslog("undeleting recording '%s'", FileName());
++#ifdef USE_STREAMDEVEXT
++ if (access(FileName(), F_OK) == 0) {
++ result = RenameVideoFile(FileName(), NewName);
++ cStatus::MsgRecordingChange(this, scAdd);
++ }
++#else
+ if (access(FileName(), F_OK) == 0)
+ result = RenameVideoFile(FileName(), NewName);
++#endif /* STREAMDEVEXT */
+ else {
+ isyslog("deleted recording '%s' vanished", FileName());
+ result = false;
+@@ -864,6 +1459,53 @@ void cRecording::ResetResume(void) const
+ resume = RESUME_NOT_INITIALIZED;
+ }
+
++#ifdef USE_LIEMIEXT
++bool cRecording::Rename(const char *newName, int *newPriority, int *newLifetime)
++{
++ bool result = false;
++ struct tm tm_r;
++ struct tm *t = localtime_r(&start, &tm_r);
++ char *localNewName = ExchangeChars(strdup(newName), true);
++ char *newFileName = strdup(cString::sprintf(NAMEFORMAT, VideoDirectory, localNewName, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, *newPriority, *newLifetime));
++ free(localNewName);
++ if (strcmp(FileName(), newFileName)) {
++ if (access(newFileName, F_OK) == 0) {
++ isyslog("recording %s already exists", newFileName);
++ }
++ else {
++ isyslog("renaming recording %s to %s", FileName(), newFileName);
++ result = MakeDirs(newFileName, true);
++ if (result)
++ result = RenameVideoFile(FileName(), newFileName);
++ if (result) {
++ priority = *newPriority;
++ lifetime = *newLifetime;
++ free(fileName);
++ fileName = strdup(newFileName);
++ free(name);
++ name = strdup(newName);
++#ifdef USE_SORTRECORDS
++ for (int i = 0; i < MAXSORTMODES; i++) {
++ free(sortBuffer[i]);
++ sortBuffer[i] = NULL;
++ }
++#else
++ free(sortBuffer);
++ sortBuffer = NULL;
++#endif /* SORTRECORDS */
++ free(titleBuffer);
++ titleBuffer = NULL;
++ }
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgRecordingChange(this, scMod);
++#endif /* STREAMDEVEXT */
++ }
++ }
++ free(newFileName);
++ return result;
++}
++#endif /* LIEMIEXT */
++
+ // --- cRecordings -----------------------------------------------------------
+
+ cRecordings Recordings;
+@@ -876,6 +1518,9 @@ cRecordings::cRecordings(bool Deleted)
+ deleted = Deleted;
+ lastUpdate = 0;
+ state = 0;
++#ifdef USE_SORTRECORDS
++ SortOrder = 1;
++#endif /* SORTRECORDS */
+ }
+
+ cRecordings::~cRecordings()
+@@ -1154,14 +1799,70 @@ cMark *cMarks::GetNext(int Position)
+ return NULL;
+ }
+
++#ifdef USE_JUMPPLAY
++// --- cMarksReload ----------------------------------------------------------
++
++#define MARKS_RELOAD_MS 10000
++
++time_t cMarksReload::lastsavetime = 0;
++
++cMarksReload::cMarksReload(const char *RecordingFileName)
++:recDir(RecordingFileName)
++{
++ struct stat sbuf;
++ if (Load(recDir) && stat(FileName(), &sbuf) == 0)
++ lastmodtime = sbuf.st_mtime;
++ else
++ lastmodtime = 0;
++ nextreload.Set(MARKS_RELOAD_MS - cTimeMs::Now() % MARKS_RELOAD_MS);
++}
++
++bool cMarksReload::Reload(void)
++{
++ // Check the timestamp of marks.vdr in 10 seconds intervals
++ // Independent but synchronized reloading of marks in two threads
++ if ((Setup.ReloadMarks && nextreload.TimedOut()) || lastsavetime > lastmodtime) {
++ nextreload.Set(MARKS_RELOAD_MS - cTimeMs::Now() % MARKS_RELOAD_MS);
++ struct stat sbuf;
++ if (stat(FileName(), &sbuf) == 0 && sbuf.st_mtime != lastmodtime) {
++ lastmodtime = sbuf.st_mtime;
++ if (Load(recDir))
++ return true;
++ }
++ }
++ return false;
++}
++
++bool cMarksReload::Save(void)
++{
++ bool ok = cMarks::Save();
++ struct stat sbuf;
++ if (ok && stat(FileName(), &sbuf) == 0)
++ lastsavetime = lastmodtime = sbuf.st_mtime;
++ return ok;
++}
++#endif /* JUMPPLAY */
++
+ // --- cRecordingUserCommand -------------------------------------------------
+
+ const char *cRecordingUserCommand::command = NULL;
+
++#ifdef USE_DVLRECSCRIPTADDON
++void cRecordingUserCommand::InvokeCommand(const char *State, const char *RecordingFileName, char *chanName)
++#else
+ void cRecordingUserCommand::InvokeCommand(const char *State, const char *RecordingFileName)
++#endif /* DVLRECSCRIPTADDON */
+ {
+ if (command) {
++#ifdef USE_DVLRECSCRIPTADDON
++ cString cmd;
++ if (chanName != NULL)
++ cmd = cString::sprintf("%s %s \"%s\" \"%s\"", command, State, *strescape(RecordingFileName, "\\\"$"), chanName);
++ else
++ cmd = cString::sprintf("%s %s \"%s\"", command, State, *strescape(RecordingFileName, "\\\"$"));
++#else
+ cString cmd = cString::sprintf("%s %s \"%s\"", command, State, *strescape(RecordingFileName, "\\\"$"));
++#endif /* DVLRECSCRIPTADDON */
+ isyslog("executing '%s'", *cmd);
+ SystemExec(cmd);
+ }
+@@ -1172,7 +1873,9 @@ void cRecordingUserCommand::InvokeComman
+ //XXX+ somewhere else???
+ // --- cIndexFile ------------------------------------------------------------
+
++#ifndef USE_LIEMIEXT
+ #define INDEXFILESUFFIX "/index.vdr"
++#endif /* LIEMIEXT */
+
+ // The number of frames to stay off the end in case of time shift:
+ #define INDEXSAFETYLIMIT 150 // frames
+@@ -1431,6 +2134,48 @@ cFileName::cFileName(const char *FileNam
+ cFileName::~cFileName()
+ {
+ Close();
++#ifdef USE_DVDARCHIVE
++
++ char fn[BUFSIZ];
++ strcpy(fn, fileName);
++
++ char *p;
++ if((p = strrchr(fn, '/'))) {
++ p[0] = 0;
++ }
++
++ cString cmd = cString::sprintf("find \"%s\" -type l -lname \"%s/*\"", fn, DVD_MOUNT_PATH);
++
++ bool isOnDvd = false;
++
++ cReadLine pipe;
++ FILE* file;
++ char* filename;
++ if ((file = popen(cmd, "r")) != (FILE *)NULL) {
++ while ((filename = pipe.Read(file)) != NULL) {
++ isOnDvd = true;
++ unlink(filename);
++ isyslog("DVD-ARCHIVE: Deleting %s", filename);
++ }
++ pclose(file);
++ }
++
++ if (isOnDvd) {
++ if (Setup.DvdTrayMode==2 || Setup.DvdTrayMode==3)
++ cmd = cString::sprintf("umount %s; eject %s", DVD_DEVICE, DVD_DEVICE);
++ else
++ cmd = cString::sprintf("umount %s", DVD_DEVICE);
++
++ SystemExec(cmd);
++
++ if (Setup.DvdSpeedLimit > 0) {
++ cmd = cString::sprintf("speedcontrol -x 999 %s", DVD_DEVICE);
++ SystemExec(cmd);
++ }
++ }
++
++#endif /* DVDARCHIVE */
++
+ free(fileName);
+ }
+
+@@ -1508,6 +2253,18 @@ cUnbufferedFile *cFileName::SetOffset(in
+ return NULL;
+ }
+
++#ifdef USE_HARDLINKCUTTER
++int cFileName::MaxFileSize() {
++ const int smallFiles = (255 * MAXVIDEOFILESIZE - 1024 * Setup.MaxRecordingSize)
++ / max(MAXVIDEOFILESIZE - Setup.MaxVideoFileSize, 1);
++
++ if (fileNumber <= smallFiles)
++ return MEGABYTE(Setup.MaxVideoFileSize);
++
++ return MEGABYTE(MAXVIDEOFILESIZE);
++}
++#endif /* HARDLINKCUTTER */
++
+ cUnbufferedFile *cFileName::NextFile(void)
+ {
+ return SetOffset(fileNumber + 1);
+@@ -1555,3 +2312,113 @@ int ReadFrame(cUnbufferedFile *f, uchar
+ LOG_ERROR;
+ return r;
+ }
++
++#ifdef USE_DVLFRIENDLYFNAMES
++char *MakeFriendlyFilename(char **buf)
++{
++ char *b, *x, *y;
++
++ if (buf == NULL || *buf == NULL)
++ return(NULL);
++
++ b = (char *)malloc(strlen(*buf) * 2);
++ x = *buf;
++ y = b;
++
++ while (*x != 0) {
++ switch (*x) {
++ case 'Ä':
++ *y = 'A';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'ä':
++ *y = 'a';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'Ö':
++ *y = 'O';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'ö':
++ *y = 'o';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'Ü':
++ *y = 'U';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'ü':
++ *y = 'u';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'ß':
++ *y = 's';
++ y++;
++ *y = 's';
++ y++; x++;
++ break;
++
++ // chars to replace
++ case ':':
++ case ';':
++ case '?':
++ case ' ':
++ case '\t':
++ *y = '_';
++ y++; x++;
++ break;
++
++ // chars to simply strip
++ case '\"':
++ case '*':
++ case '{':
++ case '}':
++ case '[':
++ case ']':
++ case '=':
++ case '<':
++ case '>':
++ case '#':
++ case '`':
++ case '|':
++ case '\\':
++ case '\n':
++ case '\r':
++ x++;
++ break;
++
++ default:
++ *y = *x;
++ y++; x++;
++ break;
++ }
++ }
++ *y = 0;
++
++ x = strdup(b);
++ free(b);
++
++ free(*buf);
++ *buf = x;
++
++ return(*buf);
++}
++#endif /* DVLFRIENDLYFNAMES */
+diff -ruNp vdr-1.6.0-2/recording.h vdr-1.6.0-2-extensions/recording.h
+--- vdr-1.6.0-2/recording.h 2007-10-14 12:11:34.000000000 +0200
++++ vdr-1.6.0-2-extensions/recording.h 2009-04-09 20:48:27.000000000 +0200
+@@ -19,6 +19,9 @@
+ #include "tools.h"
+
+ extern bool VfatFileSystem;
++#ifdef USE_LIEMIEXT
++extern bool DirOrderState;
++#endif /* LIEMIEXT */
+
+ void RemoveDeletedRecordings(void);
+ void AssertFreeDiskSpace(int Priority = 0, bool Force = false);
+@@ -52,9 +55,15 @@ private:
+ public:
+ ~cRecordingInfo();
+ tChannelID ChannelID(void) const { return channelID; }
++#ifdef USE_STREAMDEVEXT
++ const cEvent *GetEvent(void) const { return event; }
++#endif /* STREAMDEVEXT */
+ const char *ChannelName(void) const { return channelName; }
+ const char *Title(void) const { return event->Title(); }
+ const char *ShortText(void) const { return event->ShortText(); }
++#ifdef USE_GRAPHTFT
++ tEventID EventID(void) const { return event->EventID(); }
++#endif /* GRAPHTFT */
+ const char *Description(void) const { return event->Description(); }
+ const cComponents *Components(void) const { return event->Components(); }
+ const char *Aux(void) const { return aux; }
+@@ -62,16 +71,50 @@ public:
+ bool Write(FILE *f, const char *Prefix = "") const;
+ };
+
++#ifdef USE_SORTRECORDS
++#define SORTRECORDINGSVERSNUM 3
++#define MAXSORTMODES 4
++#endif /* SORTRECORDS */
++
++#ifdef USE_DVDARCHIVE
++#define MOUNT_DVD_ABORT 0
++#define MOUNT_DVD_REPLAY 1
++#define MOUNT_DVD_LAUNCH_DVD_PLUGIN 2
++#define DVD_DEVICE "/dev/cdrom"
++#define DVD_MOUNT_PATH "/tmp/vdr.dvd"
++
++#define DVD_TYPE_UNKNOWN -1
++#define DVD_TYPE_NOT_READ 0
++#define DVD_VIDEO_TYPE 1
++#define DVD_ARCHIVE_TYPE 2
++#define DVD_VIDEO_ARCHIVE_TYPE 3
++#endif /* DVDARCHIVE */
++
+ class cRecording : public cListObject {
+ friend class cRecordings;
+ private:
+ mutable int resume;
+ mutable char *titleBuffer;
++
++#ifdef USE_SORTRECORDS
++ mutable char *sortBuffer[MAXSORTMODES];
++ mutable char lastDirsFirst[MAXSORTMODES];
++#else
+ mutable char *sortBuffer;
++#endif /* SORTRECORDS */
+ mutable char *fileName;
+ mutable char *name;
+ mutable int fileSizeMB;
+ cRecordingInfo *info;
++#ifdef USE_DVDARCHIVE
++ char *dvdname;
++ char *dvdtrack;
++ char *dvdchapters;
++ bool isArchived;
++ bool isOnlyOnDvd;
++ int dvdtype;
++ bool GetDvdChaptersFromDvd(int title) const;
++#endif /* DVDARCHIVE */
+ cRecording(const cRecording&); // can't copy cRecording
+ cRecording &operator=(const cRecording &); // can't assign cRecording
+ static char *StripEpisodeName(char *s);
+@@ -88,8 +131,23 @@ public:
+ virtual int Compare(const cListObject &ListObject) const;
+ const char *Name(void) const { return name; }
+ const char *FileName(void) const;
++#ifdef USE_DVDARCHIVE
++ bool CheckFileExistence(const char* FileNameToTest, const bool useVideoDir = true) const;
++ bool GetDvdName(const char* Directory) const;
++ bool IsOnlyOnDvd(void) const { return isOnlyOnDvd; }
++ int MountDvd(void) const;
++ int GetDvdType(void) const { return dvdtype; }
++ const char *GetDvdChapters(void) const ;
++#endif /* DVDARCHIVE */
++#ifdef USE_LIEMIEXT
++ const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1, bool Original = true) const;
++#else
+ const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1) const;
++#endif /* LIEMIEXT */
+ const cRecordingInfo *Info(void) const { return info; }
++#ifdef USE_CUTTIME
++ void SetStartTime(time_t Start);
++#endif /* CUTTIME */
+ const char *PrefixFileName(char Prefix);
+ int HierarchyLevels(void) const;
+ void ResetResume(void) const;
+@@ -106,6 +164,11 @@ public:
+ // Changes the file name so that it will be visible in the "Recordings" menu again and
+ // not processed by cRemoveDeletedRecordingsThread.
+ // Returns false in case of error
++#ifdef USE_LIEMIEXT
++ bool Rename(const char *newName, int *newPriority, int *newLifetime);
++ // Changes the file name
++ // Returns false in case of error
++#endif /* LIEMIEXT */
+ };
+
+ class cRecordings : public cList<cRecording>, public cThread {
+@@ -114,6 +177,9 @@ private:
+ bool deleted;
+ time_t lastUpdate;
+ int state;
++#ifdef USE_SORTRECORDS
++ int SortOrder;
++#endif /* SORTRECORDS */
+ const char *UpdateFileName(void);
+ void Refresh(bool Foreground = false);
+ void ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0);
+@@ -144,6 +210,10 @@ public:
+ void AddByName(const char *FileName, bool TriggerUpdate = true);
+ void DelByName(const char *FileName);
+ int TotalFileSizeMB(void); ///< Only for deleted recordings!
++#ifdef USE_SORTRECORDS
++ void ToggleSortOrder(void) { SortOrder *= -1; }
++ const int GetSortOrder(void) { return SortOrder; }
++#endif /* SORTRECORDS */
+ };
+
+ extern cRecordings Recordings;
+@@ -170,6 +240,20 @@ public:
+ cMark *GetNext(int Position);
+ };
+
++#ifdef USE_JUMPPLAY
++class cMarksReload : public cMarks {
++private:
++ cString recDir;
++ cTimeMs nextreload;
++ time_t lastmodtime;
++ static time_t lastsavetime;
++public:
++ cMarksReload(const char *RecordingFileName);
++ bool Reload(void);
++ bool Save(void);
++ };
++#endif /* JUMPPLAY */
++
+ #define RUC_BEFORERECORDING "before"
+ #define RUC_AFTERRECORDING "after"
+ #define RUC_EDITEDRECORDING "edited"
+@@ -178,8 +262,15 @@ class cRecordingUserCommand {
+ private:
+ static const char *command;
+ public:
++#ifdef USE_DELTIMESHIFTREC
++ static const char *GetCommand(void) { return command; }
++#endif /* DELTIMESHIFTREC */
+ static void SetCommand(const char *Command) { command = Command; }
++#ifdef USE_DVLRECSCRIPTADDON
++ static void InvokeCommand(const char *State, const char *RecordingFileName, char *chanName = NULL);
++#else
+ static void InvokeCommand(const char *State, const char *RecordingFileName);
++#endif /* DVLRECSCRIPTADDON */
+ };
+
+ //XXX+
+@@ -195,7 +286,19 @@ public:
+ // may be slightly higher because we stop recording only before the next
+ // 'I' frame, to have a complete Group Of Pictures):
+ #define MAXVIDEOFILESIZE 2000 // MB
++#ifndef USE_HARDLINKCUTTER
+ #define MINVIDEOFILESIZE 100 // MB
++#else
++#define MINVIDEOFILESIZE 1 // MB
++
++#define MINRECORDINGSIZE 25 // GB
++#define MAXRECORDINGSIZE 500 // GB
++#define DEFAULTRECORDINGSIZE 100 // GB
++// Dynamic recording size:
++// Keep recording file size at Setup.MaxVideoFileSize for as long as possible,
++// but switch to MAXVIDEOFILESIZE early enough, so that Setup.MaxRecordingSize
++// will be reached, before recording to file 255.vdr
++#endif /* HARDLINKCUTTER */
+
+ class cIndexFile {
+ private:
+@@ -207,6 +310,9 @@ private:
+ cResumeFile resumeFile;
+ cMutex mutex;
+ bool CatchUp(int Index = -1);
++#ifdef USE_DVDARCHIVE
++ bool isOnDVD;
++#endif /* DVDARCHIVE */
+ public:
+ cIndexFile(const char *FileName, bool Record);
+ ~cIndexFile();
+@@ -236,6 +342,10 @@ public:
+ cUnbufferedFile *Open(void);
+ void Close(void);
+ cUnbufferedFile *SetOffset(int Number, int Offset = 0);
++#ifdef USE_HARDLINKCUTTER
++ int MaxFileSize();
++ // Dynamic file size for this file
++#endif /* HARDLINKCUTTER */
+ cUnbufferedFile *NextFile(void);
+ };
+
+diff -ruNp vdr-1.6.0-2/remux.c vdr-1.6.0-2-extensions/remux.c
+--- vdr-1.6.0-2/remux.c 2007-11-25 14:56:03.000000000 +0100
++++ vdr-1.6.0-2-extensions/remux.c 2009-04-09 20:48:27.000000000 +0200
+@@ -1829,8 +1829,15 @@ void cTS2PES::ts_to_pes(const uint8_t *B
+ if (Buf[1] & TS_ERROR)
+ tsErrors++;
+
++#ifdef USE_DVBSETUP
++ if (!(Buf[3] & (ADAPT_FIELD | PAY_LOAD))) {
++ dsyslog("TS packet discarded due to invalid adaption_field_control");
++ return;
++ }
++#else
+ if (!(Buf[3] & (ADAPT_FIELD | PAY_LOAD)))
+ return; // discard TS packet with adaption_field_control set to '00'.
++#endif /* DVBSETUP */
+
+ if ((Buf[3] & PAY_LOAD) && ((Buf[3] ^ ccCounter) & CONT_CNT_MASK)) {
+ // This should check duplicates and packets which do not increase the counter.
+@@ -1842,6 +1849,9 @@ void cTS2PES::ts_to_pes(const uint8_t *B
+ // These are the errors I used to get with Nova-T when antenna
+ // was not positioned correcly (not transport errors). //tvr
+ //dsyslog("TS continuity error (%d)", ccCounter);
++#ifdef USE_DVBSETUP
++ dsyslog("TS continuity error (%d)", ccCounter);
++#endif /* DVBSETUP */
+ }
+ ccCounter = Buf[3] & CONT_CNT_MASK;
+ }
+@@ -1867,6 +1877,10 @@ void cTS2PES::ts_to_pes(const uint8_t *B
+
+ if (Buf[3] & PAY_LOAD)
+ instant_repack(Buf + 4 + off, TS_SIZE - 4 - off);
++#ifdef USE_DVBSETUP
++ else if (off + 4 < 188)
++ dsyslog("adaption_field zu short or PAY_LOAD not set");
++#endif /* DVBSETUP */
+ }
+
+ // --- cRingBufferLinearPes --------------------------------------------------
+@@ -1896,12 +1910,19 @@ int cRingBufferLinearPes::DataReady(cons
+
+ #define RESULTBUFFERSIZE KILOBYTE(256)
+
++#ifdef USE_SYNCEARLY
++cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure, bool SyncEarly)
++#else
+ cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure)
++#endif /* SYNCEARLY */
+ {
+ exitOnFailure = ExitOnFailure;
+ noVideo = VPid == 0 || VPid == 1 || VPid == 0x1FFF;
+ numUPTerrors = 0;
+ synced = false;
++#ifdef USE_SYNCEARLY
++ syncEarly = SyncEarly;
++#endif /* SYNCEARLY */
+ skipped = 0;
+ numTracks = 0;
+ resultSkipped = 0;
+@@ -2105,12 +2126,26 @@ uchar *cRemux::Get(int &Count, uchar *Pi
+ }
+ }
+ else if (!synced) {
++#ifdef USE_SYNCEARLY
++ if (pt == I_FRAME || syncEarly) {
++#else
+ if (pt == I_FRAME) {
++#endif /* SYNCEARLY */
+ if (PictureType)
+ *PictureType = pt;
+ resultSkipped = i; // will drop everything before this position
++#ifdef USE_SYNCEARLY
++ if (!syncEarly)
++#endif /* SYNCEARLY */
+ SetBrokenLink(data + i, l);
+ synced = true;
++#ifdef USE_SYNCEARLY
++ if (syncEarly) {
++ if (pt == I_FRAME) // syncEarly: it's ok but there is no need to call SetBrokenLink()
++ SetBrokenLink(data + i, l);
++ else fprintf(stderr, "video: synced early\n");
++ }
++#endif /* SYNCEARLY */
+ }
+ }
+ else if (Count)
+@@ -2123,12 +2158,21 @@ uchar *cRemux::Get(int &Count, uchar *Pi
+ l = GetPacketLength(data, resultCount, i);
+ if (l < 0)
+ return resultData;
++#ifdef USE_SYNCEARLY
++ if (noVideo || !synced && syncEarly) {
++ if (!synced) {
++ if (PictureType && noVideo)
++#else
+ if (noVideo) {
+ if (!synced) {
+ if (PictureType)
++#endif /* SYNCEARLY */
+ *PictureType = I_FRAME;
+ resultSkipped = i; // will drop everything before this position
+ synced = true;
++#ifdef USE_SYNCEARLY
++ if (!noVideo && syncEarly) fprintf(stderr, "audio: synced early\n");
++#endif /* SYNCEARLY */
+ }
+ else if (Count)
+ return resultData;
+diff -ruNp vdr-1.6.0-2/remux.h vdr-1.6.0-2-extensions/remux.h
+--- vdr-1.6.0-2/remux.h 2008-09-11 13:29:43.000000000 +0200
++++ vdr-1.6.0-2-extensions/remux.h 2009-04-09 20:48:27.000000000 +0200
+@@ -38,6 +38,9 @@ private:
+ bool noVideo;
+ int numUPTerrors;
+ bool synced;
++#ifdef USE_SYNCEARLY
++ bool syncEarly;
++#endif /* SYNCEARLY */
+ int skipped;
+ cTS2PES *ts2pes[MAXTRACKS];
+ int numTracks;
+@@ -45,12 +48,18 @@ private:
+ int resultSkipped;
+ int GetPid(const uchar *Data);
+ public:
++#ifdef USE_SYNCEARLY
++ cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure = false, bool SyncEarly = false);
++#else
+ cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure = false);
++#endif /* SYNCEARLY */
+ ///< Creates a new remuxer for the given PIDs. VPid is the video PID, while
+ ///< APids, DPids and SPids are pointers to zero terminated lists of audio,
+ ///< dolby and subtitle PIDs (the pointers may be NULL if there is no such
+ ///< PID). If ExitOnFailure is true, the remuxer will initiate an "emergency
+ ///< exit" in case of problems with the data stream.
++ ///< If USE_SYNCEARLY is activated: SyncEarly causes cRemux to sync as soon
++ ///< as a video or audio frame is seen.
+ ~cRemux();
+ void SetTimeouts(int PutTimeout, int GetTimeout) { resultBuffer->SetTimeouts(PutTimeout, GetTimeout); }
+ ///< By default cRemux assumes that Put() and Get() are called from different
+diff -ruNp vdr-1.6.0-2/skinclassic.c vdr-1.6.0-2-extensions/skinclassic.c
+--- vdr-1.6.0-2/skinclassic.c 2008-02-23 11:31:58.000000000 +0100
++++ vdr-1.6.0-2-extensions/skinclassic.c 2009-04-09 20:48:27.000000000 +0200
+@@ -314,8 +314,52 @@ void cSkinClassicDisplayMenu::SetItem(co
+ for (int i = 0; i < MaxTabs; i++) {
+ const char *s = GetTabbedText(Text, i);
+ if (s) {
++#ifdef USE_LIEMIEXT
++ bool isprogressbar = false;
++ int now = 0, total = 0;
++ // check if progress bar: "[||||||| ]"
++ if ((strlen(s) > 5 && s[0] == '[' && s[strlen(s) - 1] == ']')) {
++ const char *p = s + 1;
++ // update status
++ isprogressbar = true;
++ for (; *p != ']'; ++p) {
++ // check if progressbar characters
++ if (*p == ' ' || *p == '|') {
++ // update counters
++ ++total;
++ if (*p == '|')
++ ++now;
++ }
++ else {
++ // wrong character detected; not a progressbar
++ isprogressbar = false;
++ break;
++ }
++ }
++ }
++ int xt = x0 + Tab(i);
++ if (Setup.ShowProgressBar && isprogressbar) {
++ // define x coordinates of progressbar
++ int px0 = xt;
++ int px1 = (Tab(i + 1)?Tab(i+1):x1) - 5;
++ int px = px0 + max((int)((float) now * (float) (px1 - px0) / (float) total), 1);
++ // define y coordinates of progressbar
++ int py0 = y + 4;
++ int py1 = y + lineHeight - 4;
++ // draw background
++ osd->DrawRectangle(px0, y, (Tab(i + 1)?Tab(i+1):x1) - 1, y + lineHeight - 1, ColorBg);
++ // draw progressbar
++ osd->DrawRectangle(px0, py0, px, py1, ColorFg);
++ osd->DrawRectangle(px + 1, py0, px1, py0 + 1, ColorFg);
++ osd->DrawRectangle(px + 1, py1 - 1, px1, py1, ColorFg);
++ osd->DrawRectangle(px1 - 1, py0, px1, py1, ColorFg);
++ }
++ else
++ osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x2 - xt);
++#else
+ int xt = x0 + Tab(i);
+ osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x2 - xt);
++#endif /* LIEMIEXT */
+ }
+ if (!Tab(i + 1))
+ break;
+diff -ruNp vdr-1.6.0-2/skinsttng.c vdr-1.6.0-2-extensions/skinsttng.c
+--- vdr-1.6.0-2/skinsttng.c 2008-02-23 11:23:44.000000000 +0100
++++ vdr-1.6.0-2-extensions/skinsttng.c 2009-04-09 20:48:27.000000000 +0200
+@@ -558,8 +558,52 @@ void cSkinSTTNGDisplayMenu::SetItem(cons
+ for (int i = 0; i < MaxTabs; i++) {
+ const char *s = GetTabbedText(Text, i);
+ if (s) {
++#ifdef USE_LIEMIEXT
++ bool isprogressbar = false;
++ int now = 0, total = 0;
++ // check if progress bar: "[||||||| ]"
++ if ((strlen(s) > 5 && s[0] == '[' && s[strlen(s) - 1] == ']')) {
++ const char *p = s + 1;
++ // update status
++ isprogressbar = true;
++ for (; *p != ']'; ++p) {
++ // check if progressbar characters
++ if (*p == ' ' || *p == '|') {
++ // update counters
++ ++total;
++ if (*p == '|')
++ ++now;
++ }
++ else {
++ // wrong character detected; not a progressbar
++ isprogressbar = false;
++ break;
++ }
++ }
++ }
++ int xt = x3 + 5 + Tab(i);
++ if (Setup.ShowProgressBar && isprogressbar) {
++ // define x coordinates of progressbar
++ int px0 = xt;
++ int px1 = x3 + (Tab(i + 1)?Tab(i + 1):x4-x3-5) - 1;
++ int px = px0 + max((int)((float) now * (float) (px1 - px0) / (float) total), 1);
++ // define y coordinates of progressbar
++ int py0 = y + 4;
++ int py1 = y + lineHeight - 4;
++ // draw background
++ osd->DrawRectangle(px0, y, (Tab(i + 1)?Tab(i + 1):x4-x3-5) - 1, y + lineHeight - 1, ColorBg);
++ // draw progressbar
++ osd->DrawRectangle(px0, py0, px, py1, ColorFg);
++ osd->DrawRectangle(px + 1, py0, px1, py0 + 1, ColorFg);
++ osd->DrawRectangle(px + 1, py1 - 1, px1, py1, ColorFg);
++ osd->DrawRectangle(px1 - 1, py0, px1, py1, ColorFg);
++ }
++ else
++ osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x4 - xt);
++#else
+ int xt = x3 + 5 + Tab(i);
+ osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x4 - xt);
++#endif /* LIEMIEXT */
+ }
+ if (!Tab(i + 1))
+ break;
+@@ -602,6 +646,21 @@ void cSkinSTTNGDisplayMenu::SetEvent(con
+ y += ts.Height();
+ }
+ y += font->Height();
++#ifdef USE_PARENTALRATING
++ if (!isempty(Event->GetParentalRatingString())) {
++ const cFont *font = cFont::GetFont(fontSml);
++ ts.Set(osd, xl, y, x4 - xl, y4 - y, Event->GetParentalRatingString(), font, Theme.Color(clrMenuEventShortText), Theme.Color(clrBackground));
++ y += ts.Height();
++ }
++ int i = 0;
++ while (Event->Contents(i++)) {
++ if (!isempty(Event->GetContentsString())) {
++ const cFont *font = cFont::GetFont(fontSml);
++ ts.Set(osd, xl, y, x4 - xl, y4 - y, Event->GetContentsString(), font, Theme.Color(clrMenuEventShortText), Theme.Color(clrBackground));
++ y += ts.Height();
++ }
++ }
++#endif /* PARENTALRATING */
+ if (!isempty(Event->Description())) {
+ int yt = y;
+ int yb = y4 - Roundness;
+diff -ruNp vdr-1.6.0-2/sources.c vdr-1.6.0-2-extensions/sources.c
+--- vdr-1.6.0-2/sources.c 2008-02-10 15:07:26.000000000 +0100
++++ vdr-1.6.0-2-extensions/sources.c 2009-04-09 20:48:27.000000000 +0200
+@@ -37,6 +37,9 @@ cString cSource::ToString(int Code)
+ char buffer[16];
+ char *q = buffer;
+ switch (Code & st_Mask) {
++#ifdef USE_PLUGINPARAM
++ case stPlug: *q++ = 'P'; break;
++#endif /* PLUGINPARAM */
+ case stCable: *q++ = 'C'; break;
+ case stSat: *q++ = 'S';
+ {
+@@ -56,6 +59,9 @@ int cSource::FromString(const char *s)
+ {
+ int type = stNone;
+ switch (toupper(*s)) {
++#ifdef USE_PLUGINPARAM
++ case 'P': type = stPlug; break;
++#endif /* PLUGINPARAM */
+ case 'C': type = stCable; break;
+ case 'S': type = stSat; break;
+ case 'T': type = stTerr; break;
+@@ -68,7 +74,11 @@ int cSource::FromString(const char *s)
+ int pos = 0;
+ bool dot = false;
+ bool neg = false;
++#ifdef USE_SOURCECAPS
++ while (*++s && !isblank(*s)) {
++#else
+ while (*++s) {
++#endif /* SOURCECAPS */
+ switch (toupper(*s)) {
+ case '0' ... '9': pos *= 10;
+ pos += *s - '0';
+diff -ruNp vdr-1.6.0-2/sources.conf vdr-1.6.0-2-extensions/sources.conf
+--- vdr-1.6.0-2/sources.conf 2008-09-11 13:29:43.000000000 +0200
++++ vdr-1.6.0-2-extensions/sources.conf 2009-04-09 20:48:27.000000000 +0200
+@@ -194,3 +194,7 @@ C Cable
+ # Terrestrial
+
+ T Terrestrial
++
++# Plugin PLUGINPARAM
++
++#P Plugin
+diff -ruNp vdr-1.6.0-2/sources.h vdr-1.6.0-2-extensions/sources.h
+--- vdr-1.6.0-2/sources.h 2005-05-14 11:30:41.000000000 +0200
++++ vdr-1.6.0-2-extensions/sources.h 2009-04-09 20:48:27.000000000 +0200
+@@ -16,10 +16,17 @@ class cSource : public cListObject {
+ public:
+ enum eSourceType {
+ stNone = 0x0000,
++#ifdef USE_PLUGINPARAM
++ stPlug = 0x2000,
++#endif /* PLUGINPARAM */
+ stCable = 0x4000,
+ stSat = 0x8000,
+ stTerr = 0xC000,
++#ifdef USE_PLUGINPARAM
++ st_Mask = 0xE000,
++#else
+ st_Mask = 0xC000,
++#endif /* PLUGINPARAM */
+ st_Neg = 0x0800,
+ st_Pos = 0x07FF,
+ };
+@@ -35,6 +42,9 @@ public:
+ static cString ToString(int Code);
+ static int FromString(const char *s);
+ static int FromData(eSourceType SourceType, int Position = 0, bool East = false);
++#ifdef USE_PLUGINPARAM
++ static bool IsPlug(int Code) { return (Code & st_Mask) == stPlug; }
++#endif /* PLUGINPARAM */
+ static bool IsCable(int Code) { return (Code & st_Mask) == stCable; }
+ static bool IsSat(int Code) { return (Code & st_Mask) == stSat; }
+ static bool IsTerr(int Code) { return (Code & st_Mask) == stTerr; }
+diff -ruNp vdr-1.6.0-2/status.c vdr-1.6.0-2-extensions/status.c
+--- vdr-1.6.0-2/status.c 2008-02-16 15:46:31.000000000 +0100
++++ vdr-1.6.0-2-extensions/status.c 2009-04-09 20:48:27.000000000 +0200
+@@ -29,6 +29,20 @@ void cStatus::MsgTimerChange(const cTime
+ sm->TimerChange(Timer, Change);
+ }
+
++#ifdef USE_STREAMDEVEXT
++void cStatus::MsgRecordingChange(const cRecording *Recording, eStatusChange Change)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->RecordingChange(Recording, Change);
++}
++
++void cStatus::MsgChannelChange(const cChannel *Channel, eStatusChange Change)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->ChannelChange(Channel, Change);
++}
++#endif /* STREAMDEVEXT */
++
+ void cStatus::MsgChannelSwitch(const cDevice *Device, int ChannelNumber)
+ {
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+@@ -124,3 +138,87 @@ void cStatus::MsgOsdProgramme(time_t Pre
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++
++#ifdef USE_PINPLUGIN
++bool cStatus::MsgChannelProtected(const cDevice* Device, const cChannel* Channel)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ChannelProtected(Device, Channel) == true)
++ return true;
++ return false;
++}
++
++bool cStatus::MsgReplayProtected(const cRecording* Recording, const char* Name, const char* Base, bool isDirectory, int menuView)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ReplayProtected(Recording, Name, Base, isDirectory, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgRecordingFile(const char* FileName)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->RecordingFile(FileName);
++}
++
++void cStatus::MsgTimerCreation(cTimer* Timer, const cEvent *Event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->TimerCreation(Timer, Event);
++}
++
++bool cStatus::MsgPluginProtected(cPlugin* Plugin, int menuView)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->PluginProtected(Plugin, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgUserAction(const eKeys key, const cOsdObject* Interact)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->UserAction(key, Interact);
++}
++
++bool cStatus::MsgMenuItemProtected(const char* Name, int menuView)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->MenuItemProtected(Name, menuView) == true)
++ return true;
++ return false;
++}
++#endif /* PINPLUGIN */
++
++#ifdef USE_GRAPHTFT
++void cStatus::MsgOsdSetEvent(const cEvent* event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdSetEvent(event);
++}
++
++void cStatus::MsgOsdSetRecording(const cRecording* recording)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdSetRecording(recording);
++}
++
++void cStatus::MsgOsdMenuDisplay(const char* kind)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdMenuDisplay(kind);
++}
++
++void cStatus::MsgOsdMenuDestroy()
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdMenuDestroy();
++}
++
++void cStatus::MsgOsdEventItem(const cEvent* Event, const char *Text, int Index, int Count)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdEventItem(Event, Text, Index, Count);
++}
++#endif /* GRAPHTFT */
+diff -ruNp vdr-1.6.0-2/status.h vdr-1.6.0-2-extensions/status.h
+--- vdr-1.6.0-2/status.h 2008-02-16 16:00:33.000000000 +0100
++++ vdr-1.6.0-2-extensions/status.h 2009-04-09 20:48:27.000000000 +0200
+@@ -14,8 +14,14 @@
+ #include "device.h"
+ #include "player.h"
+ #include "tools.h"
++#ifdef USE_PINPLUGIN
++#include "plugin.h"
++#endif /* PINPLUGIN */
+
+ enum eTimerChange { tcMod, tcAdd, tcDel };
++#ifdef USE_STREAMDEVEXT
++enum eStatusChange { scMod, scAdd, scDel };
++#endif /* STREAMDEVEXT */
+
+ class cTimer;
+
+@@ -30,6 +36,16 @@ protected:
+ // been added or will be deleted, respectively. In case of tcMod,
+ // Timer is NULL; this indicates that some timer has been changed.
+ // Note that tcAdd and tcDel are always also followed by a tcMod.
++#ifdef USE_STREAMDEVEXT
++ virtual void RecordingChange(const cRecording *Recording, eStatusChange Change) {}
++ // Indicates a change in the recordings settings.
++ // If Change is scAdd or scDel, Recording points to the recording that has
++ // been added or will be deleted, respectively. In case of scMod,
++ // Timer is NULL; this indicates that some timer has been changed.
++ // Note that scAdd and scDel are always also followed by a scMod.
++ virtual void ChannelChange(const cChannel *Channel, eStatusChange Change) {}
++ // Indicates a change in the channel list.
++#endif /* STREAMDEVEXT */
+ virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber) {}
+ // Indicates a channel switch on the given DVB device.
+ // If ChannelNumber is 0, this is before the channel is being switched,
+@@ -80,11 +96,45 @@ protected:
+ // The OSD displays the single line Text with the current channel information.
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+ // The OSD displays the given programme information.
++#ifdef USE_PINPLUGIN
++ virtual bool ChannelProtected(const cDevice *Device, const cChannel* Channel) { return false; }
++ // Checks if a channel is protected.
++ virtual bool ReplayProtected(const cRecording* Recording, const char* Name, const char* Base, bool isDirectory, int menuView = false) { return false; }
++ // Checks if a recording is protected.
++ virtual void RecordingFile(const char* FileName) {}
++ // The given DVB device has started recording to FileName. FileName is the name of the
++ // recording directory
++ virtual void TimerCreation(cTimer* Timer, const cEvent *Event) {}
++ // The given timer is created
++ virtual bool PluginProtected(cPlugin* Plugin, int menuView = false) { return false; }
++ // Checks if a plugin is protected.
++ virtual void UserAction(const eKeys key, const cOsdObject* Interact) {}
++ // report user action
++ virtual bool MenuItemProtected(const char* Name, int menuView = false) { return false; }
++ // Checks if a menu entry is protected.
++#endif /* PINPLUGIN */
++#ifdef USE_GRAPHTFT
++ virtual void OsdSetRecording(const cRecording* recording) {}
++ // The OSD displays the recording information.
++ virtual void OsdSetEvent(const cEvent* event) {}
++ // The OSD displays the event information.
++ virtual void OsdMenuDisplay(const char* kind) {}
++ // report menu creation
++ virtual void OsdMenuDestroy() {}
++ // report menu destruvtion
++ virtual void OsdEventItem(const cEvent* Event, const char *Text, int Index, int Count) {}
++ // The OSD displays the given single line Event as menu item at Index.
++#endif /* GRAPHTFT */
++
+ public:
+ cStatus(void);
+ virtual ~cStatus();
+ // These functions are called whenever the related status information changes:
+ static void MsgTimerChange(const cTimer *Timer, eTimerChange Change);
++#ifdef USE_STREAMDEVEXT
++ static void MsgRecordingChange(const cRecording *Recording, eStatusChange Change);
++ static void MsgChannelChange(const cChannel *Channel, eStatusChange Change);
++#endif /* STREAMDEVEXT */
+ static void MsgChannelSwitch(const cDevice *Device, int ChannelNumber);
+ static void MsgRecording(const cDevice *Device, const char *Name, const char *FileName, bool On);
+ static void MsgReplaying(const cControl *Control, const char *Name, const char *FileName, bool On);
+@@ -101,6 +151,22 @@ public:
+ static void MsgOsdTextItem(const char *Text, bool Scroll = false);
+ static void MsgOsdChannel(const char *Text);
+ static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
++#ifdef USE_PINPLUGIN
++ static bool MsgChannelProtected(const cDevice* Device, const cChannel* Channel);
++ static bool MsgReplayProtected(const cRecording* Recording, const char* Name, const char* Base, bool isDirectory, int menuView = false);
++ static void MsgRecordingFile(const char* FileName);
++ static void MsgTimerCreation(cTimer* Timer, const cEvent *Event);
++ static bool MsgPluginProtected(cPlugin* Plugin, int menuView = false);
++ static void MsgUserAction(const eKeys key, const cOsdObject* Interact);
++ static bool MsgMenuItemProtected(const char* Name, int menuView = false);
++#endif /* PINPLUGIN */
++#ifdef USE_GRAPHTFT
++ static void MsgOsdSetEvent(const cEvent* event);
++ static void MsgOsdSetRecording(const cRecording* recording);
++ static void MsgOsdMenuDisplay(const char* kind);
++ static void MsgOsdMenuDestroy();
++ static void MsgOsdEventItem(const cEvent* Event, const char *Text, int Index, int Count);
++#endif /* GRAPHTFT */
+ };
+
+ #endif //__STATUS_H
+diff -ruNp vdr-1.6.0-2/submenu.c vdr-1.6.0-2-extensions/submenu.c
+--- vdr-1.6.0-2/submenu.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/submenu.c 2009-04-09 20:48:27.000000000 +0200
+@@ -0,0 +1,952 @@
++#ifdef USE_SETUP
++/****************************************************************************
++ * DESCRIPTION:
++ * Submenu
++ *
++ * $Id: vdr-1.3.44-Setup-0.3.0.diff,v 1.1 2006/03/04 09:58:47 ralf Exp $
++ *
++ * Contact: ranga@teddycats.de
++ *
++ * Copyright (C) 2004, 2005 by Ralf Dotzert
++ *
++ * modified for the VDR Extensions Patch by zulu @vdr-portal
++ ****************************************************************************/
++
++#ifndef SUBMENU_H
++#include "submenu.h"
++#include "plugin.h"
++
++static const char* TAG_SYSTEM = "system";
++static const char* TAG_PLUGIN = "plugin";
++static const char* TAG_COMMAND = "command";
++static const char* TAG_THREAD = "thread";
++static const char* TAG_MENU = "menu";
++static const char* TAG_UNDEFINED = "undefined";
++static const char* TRUE_STR = "yes";
++
++
++//################################################################################
++//# SubMenuNode
++//################################################################################
++
++cSubMenuNode::cSubMenuNode(TiXmlElement * xml, int level, cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu)
++{
++ init();
++ _parentMenu = parentMenu;
++ _currentMenu = currentMenu;
++ _level = level;
++
++ if (xml != NULL && xml->Type() == TiXmlNode::ELEMENT) {
++ const char *tag = xml->Value();
++
++ if (cSubMenuNode::IsType(tag) != cSubMenuNode::UNDEFINED) {
++ SetType(tag);
++ SetName(xml->Attribute("name"));
++ if ((_type == COMMAND) || (_type == THREAD)) {
++ SetCommand(xml->Attribute("execute"));
++ const char * confirmStr = xml->Attribute("confirm");
++ if (confirmStr != NULL && strcmp(confirmStr, TRUE_STR) == 0)
++ _commandConfirm = true;
++ }
++ else if (_type == PLUGIN) { // Add Plugin Index
++ SetCustomTitle(xml->Attribute("title"));
++ SetPlugin();
++ }
++ else if (_type == MENU && xml->NoChildren() == false) {
++ xml = xml->FirstChildElement();
++ do {
++ cSubMenuNode *node = new cSubMenuNode(xml, level+1, &_subMenus, currentMenu);
++ _subMenus.Add(node);
++ } while ((xml=xml->NextSiblingElement()) != NULL);
++ }
++ }
++ }
++ else
++ throw "Invalid XML Node";
++}
++
++/**
++ * Construct new Node empty Node
++ *
++ *
++ */
++cSubMenuNode::cSubMenuNode(cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu)
++{
++ init();
++ _parentMenu = parentMenu;
++ _currentMenu = currentMenu;
++
++}
++
++
++/**
++ *
++ */
++void cSubMenuNode::init()
++{
++ _name = NULL;
++ _command = NULL;
++ _title = NULL;
++ _pluginMainMenuEntry = NULL;
++ _type = UNDEFINED;
++ _level = 0;
++ _parentMenu = NULL;
++ _currentMenu = NULL;
++ _pluginIndex = 0;
++ _commandConfirm = false;
++}
++
++
++cSubMenuNode::~ cSubMenuNode()
++{
++ if (_name != NULL)
++ free((void*)_name);
++ if (_command != NULL)
++ free((void*)_command);
++ if (_title != NULL)
++ free((void*)_title);
++ if (_pluginMainMenuEntry != NULL)
++ free((void*)_pluginMainMenuEntry);
++}
++
++/**
++ *
++ */
++void cSubMenuNode::SetPlugin()
++{
++ bool found = false;
++ for (int i = 0; ; i++) {
++ cPlugin *p = cPluginManager::GetPlugin(i);
++ if (p) {
++ if (strcmp(_name, p->Name()) == 0 && p->MainMenuEntry() != NULL) {
++ SetPluginMainMenuEntry(p->MainMenuEntry());
++ _pluginIndex = i;
++ found = true;
++ break;
++ }
++ }
++ else
++ break;
++ }
++
++ if (!found)
++ _type = UNDEFINED;
++}
++
++
++bool cSubMenuNode::SaveXml(TiXmlElement * root)
++{
++ bool ok = true;
++
++ if (root!=NULL) {
++ TiXmlElement *e = NULL;
++ switch(_type) {
++ case SYSTEM:
++ e = new TiXmlElement(TAG_SYSTEM);
++ e->SetAttribute("name", GetName());
++ break;
++ case COMMAND:
++ e = new TiXmlElement(TAG_COMMAND);
++ e->SetAttribute("name", GetName());
++ e->SetAttribute("execute", GetCommand());
++ if (_commandConfirm)
++ e->SetAttribute("confirm", TRUE_STR);
++ break;
++ case THREAD:
++ e = new TiXmlElement(TAG_THREAD);
++ e->SetAttribute("name", GetName());
++ e->SetAttribute("execute", GetCommand());
++ if (_commandConfirm)
++ e->SetAttribute("confirm", TRUE_STR);
++ break;
++ case PLUGIN:
++ e = new TiXmlElement(TAG_PLUGIN);
++ e->SetAttribute("name", GetName());
++ if (GetCustomTitle() != NULL && strcmp(GetCustomTitle(), "") != 0)
++ e->SetAttribute("title", GetCustomTitle());
++ break;
++ case MENU:
++ e = new TiXmlElement(TAG_MENU);
++ e->SetAttribute("name", GetName());
++ break;
++ case UNDEFINED:
++ default:
++ ok = false;
++ break;
++ }
++ if (ok) {
++ root->LinkEndChild(e);
++ if (HasSubMenus())
++ for (cSubMenuNode *node = _subMenus.First(); node; node = _subMenus.Next(node))
++ node->SaveXml(e);
++ }
++ }
++
++ return(ok);
++}
++
++
++cSubMenuNode::Type cSubMenuNode::IsType(const char *name)
++{
++ Type type = UNDEFINED;
++
++ if (strcmp(name ,TAG_SYSTEM) == 0)
++ type = cSubMenuNode::SYSTEM;
++ else if (strcmp(name ,TAG_PLUGIN) == 0)
++ type = cSubMenuNode::PLUGIN;
++ else if (strcmp(name ,TAG_COMMAND) == 0)
++ type = cSubMenuNode::COMMAND;
++ else if (strcmp(name ,TAG_THREAD) == 0)
++ type = cSubMenuNode::THREAD;
++ else if (strcmp(name ,TAG_MENU) == 0)
++ type = cSubMenuNode::MENU;
++
++ return(type);
++}
++
++void cSubMenuNode::SetType(const char * name)
++{
++ _type = IsType(name);
++}
++
++void cSubMenuNode::SetType(enum Type type)
++{
++ _type = type;
++}
++
++
++cSubMenuNode::Type cSubMenuNode::GetType()
++{
++ return(_type);
++}
++
++const char * cSubMenuNode::GetTypeAsString()
++{
++ const char *str=NULL;
++ switch(_type) {
++ case SYSTEM:
++ str = TAG_SYSTEM;
++ break;
++ case COMMAND:
++ str = TAG_COMMAND;
++ break;
++ case THREAD:
++ str = TAG_THREAD;
++ break;
++ case PLUGIN:
++ str = TAG_PLUGIN;
++ break;
++ case MENU:
++ str = TAG_MENU;
++ break;
++ case UNDEFINED:
++ str = TAG_UNDEFINED;
++ default:
++ break;
++ }
++
++ return(str);
++}
++
++void cSubMenuNode::SetCommand(const char * command)
++{
++ if (_command != NULL)
++ free((void*)_command);
++
++ if (command != NULL)
++ _command = strdup(command);
++ else
++ _command = NULL;
++}
++
++const char * cSubMenuNode::GetCommand()
++{
++ return(_command);
++}
++
++bool cSubMenuNode::CommandConfirm()
++{
++ return(_commandConfirm);
++}
++
++void cSubMenuNode::SetCommandConfirm(int val)
++{
++ if (val == 1)
++ _commandConfirm = true;
++ else
++ _commandConfirm = false;
++}
++
++void cSubMenuNode::SetCustomTitle(const char * title)
++{
++ if (_title != NULL)
++ free((void*)_title);
++
++ if (title != NULL)
++ _title = strdup(title);
++ else
++ _title = NULL;
++}
++
++const char * cSubMenuNode::GetCustomTitle()
++{
++ return(_title);
++}
++
++void cSubMenuNode::SetName(const char * name)
++{
++ if (_name)
++ free ((void*)_name);
++
++ if (name != NULL)
++ _name = strdup(name);
++ else
++ _name = NULL;
++}
++
++const char * cSubMenuNode::GetName()
++{
++ return(_name);
++}
++
++int cSubMenuNode::GetLevel()
++{
++ return(_level);
++}
++
++void cSubMenuNode::SetLevel(int level)
++{
++ _level = level;
++ if (HasSubMenus()) { //Adjust Levels of Subnodes
++ for (cSubMenuNode *node = _subMenus.First(); node; node = _subMenus.Next(node))
++ node->SetLevel(level+1);
++ }
++}
++
++int cSubMenuNode::GetPluginIndex()
++{
++ return(_pluginIndex);
++}
++
++void cSubMenuNode::SetPluginIndex(int index)
++{
++ _pluginIndex = index;
++}
++
++void cSubMenuNode::SetPluginMainMenuEntry(const char * mainMenuEntry)
++{
++ if (_pluginMainMenuEntry != NULL)
++ free((void*)_pluginMainMenuEntry);
++
++ if (_title != NULL && strcmp(_title, "") != 0)
++ _pluginMainMenuEntry = strdup(_title);
++ else if (mainMenuEntry != NULL)
++ _pluginMainMenuEntry = strdup(mainMenuEntry);
++ else
++ _pluginMainMenuEntry = NULL;
++}
++
++const char * cSubMenuNode::GetPluginMainMenuEntry()
++{
++ return(_pluginMainMenuEntry);
++}
++
++
++
++cSubMenuNodes * cSubMenuNode::GetParentMenu()
++{
++ return(_parentMenu);
++}
++
++void cSubMenuNode::SetParentMenu(cSubMenuNodes * parent)
++{
++ _parentMenu = parent;
++}
++
++cSubMenuNodes * cSubMenuNode::GetCurrentMenu()
++{
++ return(_currentMenu);
++}
++
++void cSubMenuNode::SetCurrentMenu(cSubMenuNodes * current)
++{
++ _currentMenu = current;
++}
++
++
++cSubMenuNodes * cSubMenuNode::GetSubMenus()
++{
++ return(&_subMenus);
++}
++
++bool cSubMenuNode::HasSubMenus()
++{
++ if (_subMenus.Count() > 0)
++ return(true);
++ else
++ return(false);
++}
++
++
++void cSubMenuNode::Print(int index)
++{
++ for (int i = 0; i < index; i++)
++ printf(" ");
++
++ printf("Name=%s Type=%s Level=%d", _name, GetTypeAsString(), _level);
++ if (_type == COMMAND || _type == THREAD)
++ printf(" Command=%s", _command);
++ else if (_type == PLUGIN && _title != NULL)
++ printf(" Title=%s", _title);
++ printf("\n");
++
++ for (cSubMenuNode *node = _subMenus.First(); node; node = _subMenus.Next(node))
++ node->Print(index+4);
++}
++
++
++//################################################################################
++//#
++//################################################################################
++cSubMenu::cSubMenu()
++{
++ _menuSuffix = NULL;
++ _fname = NULL;
++ _commandResult = NULL;
++ _currentMenuTree = &_menuTree;
++ _currentParentMenuTree = NULL;
++#ifdef USE_PINPLUGIN
++ _currentParentIndex = -1;
++#endif /* PINPLUGIN */
++ _nodeArray = NULL;
++ _nrNodes = 0;
++}
++
++
++cSubMenu::~cSubMenu()
++{
++ if (_menuSuffix)
++ free(_menuSuffix);
++ if (_fname)
++ free(_fname);
++ if (_commandResult)
++ free(_commandResult);
++ if (_nodeArray)
++ free(_nodeArray);
++ _nrNodes = 0;
++}
++
++
++bool cSubMenu::LoadXml(char *fname)
++{
++ TiXmlDocument xmlDoc = TiXmlDocument(fname);
++ TiXmlElement *root = NULL;
++ cSubMenuNode *node = NULL;
++
++ bool ok = true;
++ //Clear previously loaded Menu
++ if (_fname != NULL)
++ free(_fname);
++ _menuTree.Clear();
++ _fname = strdup(fname);
++
++ if ((ok = xmlDoc.LoadFile())) {
++ if ((root = xmlDoc.FirstChildElement("menus")) != NULL) {
++ char *tmp = NULL;
++ if ((tmp = (char*)root->Attribute("suffix")) == NULL)
++ asprintf(&_menuSuffix, " "); // set default menuSuffix // asprintf(&_menuSuffix, " ...");
++ else
++ asprintf(&_menuSuffix, tmp);
++
++ if ((root = root->FirstChildElement()) != NULL) {
++ do {
++ try {
++ node = new cSubMenuNode(root, 0, &_menuTree, NULL);
++ _menuTree.Add(node);
++ }
++ catch (char *message) {
++ esyslog("ERROR: while decoding XML Node");
++ ok = false;
++ }
++ } while (ok == true && (root = root->NextSiblingElement()) != NULL);
++ addMissingPlugins();
++ removeUndefinedNodes();
++ }
++ }
++ else {
++ esyslog("ERROR: in %s, missing Tag <menus>\n", fname);
++ ok = false;
++ }
++ }
++ else {
++ esyslog("ERROR: in %s : %s Col=%d Row=%d\n",
++ fname,
++ xmlDoc.ErrorDesc(),
++ xmlDoc.ErrorCol(),
++ xmlDoc.ErrorRow());
++ ok = false;
++ }
++
++ return(ok);
++}
++
++
++bool cSubMenu::SaveXml()
++{
++ return(SaveXml(_fname));
++}
++
++
++bool cSubMenu::SaveXml(char *fname)
++{
++ bool ok = true;
++
++ if (_fname != NULL) {
++ TiXmlDocument xml = TiXmlDocument(fname);
++ TiXmlComment comment;
++ comment.SetValue("\n\
++- VDR Menu-Configuration File\n\
++-\n\
++-\n\
++- Example:\n\
++-\n\
++ <menus>\n\
++ <system name=\"Schedule\" />\n\
++ <system name=\"Channels\" />\n\
++ <system name=\"Timers\" />\n\
++ <system name=\"Recordings\" />\n\
++ <menu name=\"System\">\n\
++ <system name=\"Setup\" />\n\
++ <system name=\"Commands\" />\n\
++ <plugin name=\"setup\" title=\"My Setup\" />\n\
++ <command name=\"myCommand1\" execute=\"/usr/bin/mycommand1\" />\n\
++ <command name=\"myCommand2\" execute=\"/usr/bin/mycommand2\" confirm=\"yes\" />\n\
++ <thread name=\"myCommand3\" execute=\"/usr/bin/mycommand3\" confirm=\"yes\" />\n\
++ <plugin name=\"epgsearch\" title=\"myProgram\" />\n\
++ <menu name=\"mySubSubMenu\">\n\
++ ...\n\
++ </menu>\n\
++ </menu>\n\
++ <menu name=\"Suche\">\n\
++ <plugin name=\"epgsearch\" />\n\
++ ...\n\
++ </menu>\n\
++ </menus>\n\
++");
++
++ TiXmlElement root("menus");
++ root.SetAttribute("suffix", _menuSuffix);
++ for (cSubMenuNode *node = _menuTree.First(); node; node = _menuTree.Next(node))
++ node->SaveXml(&root);
++
++ if (xml.InsertEndChild(comment) != NULL && xml.InsertEndChild(root) != NULL)
++ ok = xml.SaveFile(fname);
++ }
++ else
++ ok = false;
++
++ return(ok);
++}
++
++
++
++cSubMenuNodes * cSubMenu::GetMenuTree()
++{
++ return(_currentMenuTree);
++}
++
++
++void cSubMenu::PrintMenuTree()
++{
++ for (cSubMenuNode *node = _menuTree.First(); node; node = _menuTree.Next(node))
++ node->Print();
++}
++
++
++int cSubMenu::GetNrOfNodes()
++{
++ if (_nrNodes == 0) {
++ if ((_nrNodes = countNodes(&_menuTree)) > 0) {
++ _nodeArray = (cSubMenuNode**) malloc(sizeof(cSubMenuNode*)*_nrNodes);
++ int index = 0;
++ tree2Array(&_menuTree, index);
++ }
++ }
++
++ return(_nrNodes);
++}
++
++
++/**
++ * returns the specified node within the current menu
++ * @param index position in the current menu
++ * @return node or null if not found
++ */
++cSubMenuNode * cSubMenu::GetNode(int index)
++{
++ cSubMenuNode *node = NULL;
++ if (_currentMenuTree == NULL || (node=_currentMenuTree->Get(index)) == NULL)
++ esyslog("ERROR: illegal call of cSubMenu::GetNode(%d)", index);
++
++ return(node);
++}
++
++
++/**
++ * Get the specified Node
++ * @param index specfies the absolut indes in the list of all nodes
++ * @return node or NULL if not found
++ */
++cSubMenuNode * cSubMenu::GetAbsNode(int index)
++{
++ cSubMenuNode *node = NULL;
++ GetNrOfNodes();
++ if (_nrNodes > 0 && index >= 0 && index < _nrNodes)
++ node = _nodeArray[index];
++
++ return(node);
++}
++
++
++#ifdef USE_PINPLUGIN
++bool cSubMenu::Down(cSubMenuNode *node, int currentIndex)
++#else
++bool cSubMenu::Down(int index)
++#endif /* PINPLUGIN */
++{
++ bool ok = true;
++#ifdef USE_PINPLUGIN
++ if (_currentMenuTree != NULL && node && node->GetType() == cSubMenuNode::MENU) {
++#else
++ cSubMenuNode *node = NULL;
++
++ if (_currentMenuTree != NULL && (node=_currentMenuTree->Get(index)) != NULL && node->GetType() == cSubMenuNode::MENU) {
++#endif /* PINPLUGIN */
++ _currentParentMenuTree = _currentMenuTree;
++#ifdef USE_PINPLUGIN
++ _currentParentIndex = currentIndex;
++#endif /* PINPLUGIN */
++ _currentMenuTree = node->GetSubMenus();
++ }
++ else {
++ ok = false;
++#ifdef USE_PINPLUGIN
++ esyslog("ERROR: illegal call of cSubMenu::Down");
++#else
++ esyslog("ERROR: illegal call of cSubMenu::Down(%d)", index);
++#endif /* PINPLUGIN */
++ }
++
++ return(ok);
++}
++
++bool cSubMenu::Up(int *parentIndex)
++{
++ bool ok = true;
++
++ if (_currentMenuTree != NULL && parentIndex != NULL) {
++#ifndef USE_PINPLUGIN
++ cSubMenuNode *node = NULL;
++#endif /* PINPLUGIN */
++ *parentIndex = 0;
++#ifdef USE_PINPLUGIN
++ if (_currentParentIndex >= 0)
++ *parentIndex = _currentParentIndex;
++#else
++ if (_currentParentMenuTree != NULL)
++ for (int i = 0; (node = _currentParentMenuTree->Get(i)) != NULL; i++) {
++ if (_currentMenuTree == node->GetSubMenus()) {
++ *parentIndex = i;
++ break;
++ }
++ }
++#endif /* PINPLUGIN */
++
++ _currentMenuTree = _currentParentMenuTree;
++ if (_currentMenuTree != NULL)
++ _currentParentMenuTree = _currentMenuTree->Get(0)->GetParentMenu();
++ else
++ ok = false;
++ }
++ else {
++ ok = false;
++ esyslog("ERROR: illegal call of cSubMenu::Up()");
++ }
++
++ return(ok);
++}
++
++const char * cSubMenu::ExecuteCommand(const char * cmd)
++{
++ free(_commandResult);
++ _commandResult = NULL;
++
++ dsyslog("executing command '%s'", cmd);
++ FILE *p = popen(cmd, "r");
++ if (p) {
++ int l = 0;
++ int c;
++ while ((c = fgetc(p)) != EOF) {
++ if (l % 20 == 0)
++ _commandResult = (char *)realloc(_commandResult, l + 21);
++ _commandResult[l++] = c;
++ }
++ if (_commandResult)
++ _commandResult[l] = 0;
++ pclose(p);
++ }
++ else
++ esyslog("ERROR: can't open pipe for command '%s'", cmd);
++
++ return _commandResult;
++}
++
++/**
++ * Move Menu Entry to new Position
++ * @param index index of menu entry to move
++ * @param toIndex index of destination
++ * @param where After ore before the destination index
++ */
++void cSubMenu::MoveMenu(int index, int toIndex, enum Where where)
++{
++ if (index < 0 || index > _nrNodes || // invalid index is ignored
++ toIndex < 0 || toIndex > _nrNodes || index == toIndex)
++ return;
++
++ cSubMenuNode *srcNode = GetAbsNode(index);
++ cSubMenuNode *destNode = GetAbsNode(toIndex);
++
++ if (where == cSubMenu::INTO && destNode->GetType() != cSubMenuNode::MENU)
++ return;
++
++ if (where == cSubMenu::INTO) {
++ if (destNode->GetType() == cSubMenuNode::MENU) {
++ srcNode->GetCurrentMenu()->Del(srcNode, false);
++ srcNode->SetLevel(destNode->GetLevel()+1);
++ srcNode->SetParentMenu(destNode->GetCurrentMenu());
++ srcNode->SetCurrentMenu(destNode->GetSubMenus());
++
++ destNode->GetSubMenus()->Add(srcNode);
++ reloadNodeArray();
++ }
++ }
++ else {
++ srcNode->GetCurrentMenu()->Del(srcNode, false);
++ srcNode->SetLevel(destNode->GetLevel());
++ srcNode->SetParentMenu(destNode->GetParentMenu());
++ srcNode->SetCurrentMenu(destNode->GetCurrentMenu());
++
++ if (where == cSubMenu::BEHIND) {
++ destNode->GetCurrentMenu()->Add(srcNode, GetAbsNode(toIndex));
++ reloadNodeArray();
++ }
++ else {
++ destNode->GetCurrentMenu()->Ins(srcNode, GetAbsNode(toIndex));
++ reloadNodeArray();
++ }
++ }
++}
++
++/**
++ * Create a new Menu Entry
++ * @param index index of destination
++ * @param menuTitle Titel of new Menu entry
++ */
++void cSubMenu::CreateMenu(int index, const char * menuTitle)
++{
++ if (index >= 0 && index < _nrNodes) {
++ cSubMenuNode *srcNode = GetAbsNode(index);
++ if (srcNode != NULL) {
++ cSubMenuNode *newNode = new cSubMenuNode(srcNode->GetParentMenu(), srcNode->GetCurrentMenu());
++ newNode->SetLevel(srcNode->GetLevel());
++ newNode->SetName(menuTitle);
++ newNode->SetType(cSubMenuNode::MENU);
++ newNode->SetParentMenu(srcNode->GetParentMenu());
++ newNode->SetCurrentMenu(srcNode->GetCurrentMenu());
++
++ srcNode->GetCurrentMenu()->Add(newNode, GetAbsNode(index));
++ reloadNodeArray();
++ }
++ }
++}
++
++/**
++ * delete the specified entry, or subtree if the specified entry is a menu
++ * @param index destion index
++ */
++void cSubMenu::DeleteMenu(int index)
++{
++ if (index >= 0 && index < _nrNodes) {
++ cSubMenuNode *srcNode = GetAbsNode(index);
++ srcNode->GetCurrentMenu()->Del(srcNode, true);
++ reloadNodeArray();
++ }
++}
++
++
++// Private Methods
++
++int cSubMenu::countNodes(cSubMenuNodes * tree)
++{
++ int count = 0;
++ if (tree != NULL) {
++ for (cSubMenuNode *node = tree->First(); node; node = tree->Next(node)) {
++ count++;
++ if (node->HasSubMenus())
++ count += countNodes(node->GetSubMenus());
++ }
++ }
++ return(count);
++}
++
++
++void cSubMenu::tree2Array(cSubMenuNodes * tree, int &index)
++{
++ if (tree != NULL) {
++ for (cSubMenuNode *node = tree->First(); node; node = tree->Next(node)) {
++ _nodeArray[index++]=node;
++ if (node->HasSubMenus())
++ tree2Array(node->GetSubMenus(), index);
++ }
++ }
++
++}
++
++bool cSubMenu::IsPluginInMenu(const char * name)
++{
++ bool found = false;
++ for (int i = 0; i < _nrNodes && found == false; i++) {
++ cSubMenuNode *node = GetAbsNode(i);
++ if (node != NULL && node->GetType() == cSubMenuNode::PLUGIN && strcmp(name, node->GetName()) == 0)
++ found = true;
++ }
++ return(found);
++}
++
++/**
++ * Adds the given plugin to the Menu-Tree if not allready in List
++ * @param name specifies the name of the plugin
++ */
++void cSubMenu::AddPlugin(const char * name)
++{
++ if (! IsPluginInMenu(name)) {
++ cSubMenuNode *node = new cSubMenuNode(&_menuTree, NULL);
++ node->SetName(name);
++ node->SetType("plugin");
++ node->SetPlugin();
++ _menuTree.Add(node);
++ }
++}
++
++void cSubMenu::addMissingPlugins()
++{
++ _nrNodes = GetNrOfNodes();
++ for (int i = 0; ; i++) {
++ cPlugin *p = cPluginManager::GetPlugin(i);
++ if (p)
++ AddPlugin(p->Name());
++ else
++ break;
++ }
++ reloadNodeArray();
++}
++
++/**
++ * Adds the given command to the Menu-Tree
++ * @param name specifies the name of the command
++ */
++void cSubMenu::CreateCommand(int index, const char * name, const char * execute, int confirm)
++{
++ if (index >= 0 && index < _nrNodes) {
++ cSubMenuNode *srcNode = GetAbsNode(index);
++ if (srcNode != NULL) {
++ cSubMenuNode *newNode = new cSubMenuNode(srcNode->GetParentMenu(), srcNode->GetCurrentMenu());
++ newNode->SetLevel(srcNode->GetLevel());
++ newNode->SetName(name);
++ newNode->SetType("command");
++ newNode->SetCommand(execute);
++ newNode->SetCommandConfirm(confirm);
++ newNode->SetParentMenu(srcNode->GetParentMenu());
++ newNode->SetCurrentMenu(srcNode->GetCurrentMenu());
++
++ srcNode->GetCurrentMenu()->Add(newNode, GetAbsNode(index));
++ reloadNodeArray();
++ }
++ }
++}
++
++void cSubMenu::CreateThread(int index, const char * name, const char * execute, int confirm)
++{
++ if (index >= 0 && index < _nrNodes) {
++ cSubMenuNode *srcNode = GetAbsNode(index);
++ if (srcNode != NULL) {
++ cSubMenuNode *newNode = new cSubMenuNode(srcNode->GetParentMenu(), srcNode->GetCurrentMenu());
++ newNode->SetLevel(srcNode->GetLevel());
++ newNode->SetName(name);
++ newNode->SetType("thread");
++ newNode->SetCommand(execute);
++ newNode->SetCommandConfirm(confirm);
++ newNode->SetParentMenu(srcNode->GetParentMenu());
++ newNode->SetCurrentMenu(srcNode->GetCurrentMenu());
++
++ srcNode->GetCurrentMenu()->Add(newNode, GetAbsNode(index));
++ reloadNodeArray();
++ }
++ }
++}
++
++/**
++ * reloads the internal Array of Nodes
++ */
++void cSubMenu::reloadNodeArray()
++{
++ if (_nrNodes > 0)
++ free(_nodeArray);
++ _nodeArray = NULL;
++ _nrNodes = 0;
++ _nrNodes = GetNrOfNodes();
++}
++
++/**
++ * remove Undefined Nodes
++ */
++void cSubMenu::removeUndefinedNodes()
++{
++ bool remove = false;
++
++ reloadNodeArray();
++ for (int i = 0; i < _nrNodes; i++) {
++ cSubMenuNode *node = GetAbsNode(i);
++ if (node != NULL && node->GetType() == cSubMenuNode::UNDEFINED) {
++ cSubMenuNodes *pMenu = node->GetCurrentMenu();
++ pMenu->Del(node, true);
++ remove = true;
++ }
++ }
++ if (remove)
++ reloadNodeArray();
++}
++
++
++/**
++* Retrieves the Menutitel of the parent Menu
++*/
++const char *cSubMenu::GetParentMenuTitel()
++{
++ const char * result = "";
++
++ if (_currentMenuTree != NULL && _currentParentMenuTree != NULL) {
++ cSubMenuNode *node = NULL;
++ for (int i = 0; (node = _currentParentMenuTree->Get(i)) != NULL; i++) {
++ if (_currentMenuTree == node->GetSubMenus()) {
++ result = node->GetName();
++ break;
++ }
++ }
++ }
++
++ return(result);
++}
++
++#endif
++#endif /* SETUP */
+diff -ruNp vdr-1.6.0-2/submenu.h vdr-1.6.0-2-extensions/submenu.h
+--- vdr-1.6.0-2/submenu.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/submenu.h 2009-04-09 20:48:27.000000000 +0200
+@@ -0,0 +1,160 @@
++#ifdef USE_SETUP
++/****************************************************************************
++ * DESCRIPTION:
++ * Submenu
++ *
++ * $Id: vdr-1.3.44-Setup-0.3.0.diff,v 1.1 2006/03/04 09:58:47 ralf Exp $
++ *
++ * Contact: ranga@teddycats.de
++ *
++ * Copyright (C) 2004, 2005 by Ralf Dotzert
++ *
++ * modified for the VDR Extensions Patch by zulu @vdr-portal
++ ****************************************************************************/
++
++#ifndef SUBMENU_H
++#define SUBMENU_H
++
++#include "thread.h"
++#include "tools.h"
++#include "tinystr.h"
++
++class cSubMenuNode;
++class cSubMenuNodes;
++class cSubMenu;
++
++
++class cSubMenuNodes : public cList<cSubMenuNode> {};
++
++// execute cmd thread
++class cExecCmdThread : public cThread {
++private:
++ char *ExecCmd;
++protected:
++ virtual void Action(void) {
++ if (system(ExecCmd) == 0)
++ esyslog("%s - finished", ExecCmd);
++ delete(this);
++ };
++public:
++ cExecCmdThread(char *cmd) {
++ asprintf(&ExecCmd, "%s", cmd);
++ }
++ cExecCmdThread(const char *cmd) {
++ asprintf(&ExecCmd, "%s", cmd);
++ }
++ ~cExecCmdThread() {
++ free(ExecCmd);
++ };
++ };
++
++//################################################################################
++//# SubMenuNode
++//################################################################################
++class cSubMenuNode : public cListObject {
++public:
++ enum Type { UNDEFINED, SYSTEM, COMMAND, THREAD, PLUGIN, MENU };
++ cSubMenuNode(TiXmlElement * xml, int level, cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu);
++ cSubMenuNode(cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu);
++ ~cSubMenuNode();
++ bool SaveXml(TiXmlElement * root);
++ static cSubMenuNode::Type IsType(const char *name);
++ void SetType(const char *name);
++ void SetType(enum Type type);
++ void SetPlugin();
++ cSubMenuNode::Type GetType();
++ const char *GetTypeAsString();
++ void SetCommand(const char *command);
++ bool CommandConfirm();
++ void SetCommandConfirm(int val);
++ const char *GetCommand();
++ void SetCustomTitle(const char *title);
++ const char *GetCustomTitle();
++ void SetName(const char *name);
++ const char*GetName();
++ int GetLevel();
++ void SetLevel(int level);
++ int GetPluginIndex();
++ void SetPluginIndex(int index);
++ void SetPluginMainMenuEntry(const char *mainMenuEntry);
++ const char *GetPluginMainMenuEntry();
++ cSubMenuNodes *GetParentMenu();
++ void SetParentMenu(cSubMenuNodes *parent);
++ cSubMenuNodes *GetCurrentMenu();
++ void SetCurrentMenu(cSubMenuNodes *current);
++ cSubMenuNodes *GetSubMenus();
++ bool HasSubMenus();
++ void Print(int index = 0);
++private:
++ Type _type;
++ int _level;
++ // Plugin Variables
++ int _pluginIndex;
++ const char *_pluginMainMenuEntry;
++ // common
++ const char *_name;
++ const char *_command;
++ bool _commandConfirm;
++ const char *_title;
++ cSubMenuNodes _subMenus;
++ cSubMenuNodes *_parentMenu;
++ cSubMenuNodes *_currentMenu;
++ void init();
++ };
++
++
++//################################################################################
++//# SubMenu Class
++//################################################################################
++class cSubMenu {
++public:
++ cSubMenu();
++ ~cSubMenu();
++ enum Where { BEFORE, BEHIND, INTO};
++ bool LoadXml(char *fname);
++ bool SaveXml(char *fname);
++ bool SaveXml();
++ cSubMenuNodes *GetMenuTree();
++ bool Up(int *ParentIndex);
++#ifdef USE_PINPLUGIN
++ bool Down(cSubMenuNode* node, int currentIndex);
++#else
++ bool Down(int index);
++#endif /* PINPLUGIN */
++ int GetNrOfNodes();
++ cSubMenuNode* GetAbsNode(int index);
++ cSubMenuNode* GetNode(int index);
++ void PrintMenuTree();
++ bool IsPluginInMenu(const char *name);
++ void AddPlugin(const char *name);
++ void CreateCommand(int index, const char *name, const char *execute, int confirm);
++ void CreateThread(int index, const char *name, const char *execute, int confirm);
++ const char *ExecuteCommand(const char *command);
++ void MoveMenu(int index, int toindex, enum Where);
++ void CreateMenu(int index, const char *menuTitle);
++ void DeleteMenu(int index);
++ char *GetMenuSuffix() { return _menuSuffix; }
++ void SetMenuSuffix(char *suffix) { if (_menuSuffix) free(_menuSuffix); asprintf(&_menuSuffix, suffix); }
++ bool isTopMenu() { return (_currentParentMenuTree == NULL); }
++ const char *GetParentMenuTitel();
++private:
++ cSubMenuNodes _menuTree;
++ cSubMenuNodes *_currentMenuTree;
++ cSubMenuNodes *_currentParentMenuTree;
++#ifdef USE_PINPLUGIN
++ int _currentParentIndex;
++#endif /* PINPLUGIN */
++ char *_fname;
++ char *_commandResult;
++ int _nrNodes;
++ cSubMenuNode **_nodeArray;
++ char *_menuSuffix;
++ int countNodes(cSubMenuNodes *tree);
++ void tree2Array(cSubMenuNodes *tree, int &index);
++ void addMissingPlugins();
++ void reloadNodeArray();
++ void removeUndefinedNodes();
++ };
++
++#endif //__SUBMENU_H
++#endif /* SETUP */
+diff -ruNp vdr-1.6.0-2/svdrp.c vdr-1.6.0-2-extensions/svdrp.c
+--- vdr-1.6.0-2/svdrp.c 2008-09-11 13:29:43.000000000 +0200
++++ vdr-1.6.0-2-extensions/svdrp.c 2009-04-09 20:48:27.000000000 +0200
+@@ -39,6 +39,9 @@
+ #include "timers.h"
+ #include "tools.h"
+ #include "videodir.h"
++#ifdef USE_STREAMDEVEXT
++#include "status.h"
++#endif /* STREAMDEVEXT */
+
+ // --- cSocket ---------------------------------------------------------------
+
+@@ -296,6 +299,10 @@ const char *HelpPages[] = {
+ "REMO [ on | off ]\n"
+ " Turns the remote control on or off. Without a parameter, the current\n"
+ " status of the remote control is reported.",
++#ifdef USE_LIEMIEXT
++ "RENR <number> <new name>\n"
++ " Rename recording. Number must be the Number as returned by LSTR command.",
++#endif /* LIEMIEXT */
+ "SCAN\n"
+ " Forces an EPG scan. If this is a single DVB device system, the scan\n"
+ " will be done on the primary device unless it is currently recording.",
+@@ -616,6 +623,9 @@ void cSVDRP::CmdDELC(const char *Option)
+ Channels.ReNumber();
+ Channels.SetModified(true);
+ isyslog("channel %s deleted", Option);
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgChannelChange(NULL, scDel);
++#endif /* STREAMDEVEXT */
+ if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) {
+ if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring())
+ Channels.SwitchTo(CurrentChannel->Number());
+@@ -1135,6 +1145,9 @@ void cSVDRP::CmdMODC(const char *Option)
+ Channels.ReNumber();
+ Channels.SetModified(true);
+ isyslog("modifed channel %d %s", channel->Number(), *channel->ToText());
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgChannelChange(channel, scMod);
++#endif /* STREAMDEVEXT */
+ Reply(250, "%d %s", channel->Number(), *channel->ToText());
+ }
+ else
+@@ -1221,6 +1234,9 @@ void cSVDRP::CmdMOVC(const char *Option)
+ else
+ cDevice::SetCurrentChannel(CurrentChannel);
+ }
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgChannelChange(ToChannel, scMod);
++#endif /* STREAMDEVEXT */
+ isyslog("channel %d moved to %d", FromNumber, ToNumber);
+ Reply(250,"Channel \"%d\" moved to \"%d\"", From, To);
+ }
+@@ -1264,6 +1280,9 @@ void cSVDRP::CmdNEWC(const char *Option)
+ Channels.ReNumber();
+ Channels.SetModified(true);
+ isyslog("new channel %d %s", channel->Number(), *channel->ToText());
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgChannelChange(channel, scAdd);
++#endif /* STREAMDEVEXT */
+ Reply(250, "%d %s", channel->Number(), *channel->ToText());
+ }
+ else
+@@ -1472,6 +1491,40 @@ void cSVDRP::CmdSCAN(const char *Option)
+ Reply(250, "EPG scan triggered");
+ }
+
++#ifdef USE_LIEMIEXT
++void cSVDRP::CmdRENR(const char *Option)
++{
++ bool recordings = Recordings.Update(true);
++ if (recordings) {
++ if (*Option) {
++ char *tail;
++ int n = strtol(Option, &tail, 10);
++ cRecording *recording = Recordings.Get(n - 1);
++ if (recording && tail && tail != Option) {
++ int priority = recording->priority;
++ int lifetime = recording->lifetime;
++ char *oldName = strdup(recording->Name());
++ tail = skipspace(tail);
++ if (recording->Rename(tail, &priority, &lifetime)) {
++ Reply(250, "Renamed \"%s\" to \"%s\"", oldName, recording->Name());
++ Recordings.ChangeState();
++ Recordings.TouchUpdate();
++ }
++ else
++ Reply(501, "Renaming \"%s\" to \"%s\" failed", oldName, tail);
++ free(oldName);
++ }
++ else
++ Reply(501, "Recording not found or wrong syntax");
++ }
++ else
++ Reply(501, "Missing Input settings");
++ }
++ else
++ Reply(550, "No recordings available");
++}
++#endif /* LIEMIEXT */
++
+ void cSVDRP::CmdSTAT(const char *Option)
+ {
+ if (*Option) {
+@@ -1587,6 +1640,9 @@ void cSVDRP::Execute(char *Cmd)
+ else if (CMD("PLUG")) CmdPLUG(s);
+ else if (CMD("PUTE")) CmdPUTE(s);
+ else if (CMD("REMO")) CmdREMO(s);
++#ifdef USE_LIEMIEXT
++ else if (CMD("RENR")) CmdRENR(s);
++#endif /* LIEMIEXT */
+ else if (CMD("SCAN")) CmdSCAN(s);
+ else if (CMD("STAT")) CmdSTAT(s);
+ else if (CMD("UPDT")) CmdUPDT(s);
+diff -ruNp vdr-1.6.0-2/svdrp.h vdr-1.6.0-2-extensions/svdrp.h
+--- vdr-1.6.0-2/svdrp.h 2007-04-30 14:28:28.000000000 +0200
++++ vdr-1.6.0-2-extensions/svdrp.h 2009-04-09 20:48:27.000000000 +0200
+@@ -79,6 +79,9 @@ private:
+ void CmdPLUG(const char *Option);
+ void CmdPUTE(const char *Option);
+ void CmdREMO(const char *Option);
++#ifdef USE_LIEMIEXT
++ void CmdRENR(const char *Option);
++#endif /* LIEMIEXT */
+ void CmdSCAN(const char *Option);
+ void CmdSTAT(const char *Option);
+ void CmdUPDT(const char *Option);
+diff -ruNp vdr-1.6.0-2/timers.c vdr-1.6.0-2-extensions/timers.c
+--- vdr-1.6.0-2/timers.c 2008-09-11 13:29:39.000000000 +0200
++++ vdr-1.6.0-2-extensions/timers.c 2009-04-09 20:48:27.000000000 +0200
+@@ -13,6 +13,9 @@
+ #include "device.h"
+ #include "i18n.h"
+ #include "libsi/si.h"
++#ifdef USE_LIVEBUFFER
++#include "livebuffer.h"
++#endif /* LIVEBUFFER */
+ #include "recording.h"
+ #include "remote.h"
+ #include "status.h"
+@@ -46,6 +49,9 @@ cTimer::cTimer(bool Instant, bool Pause,
+ stop -= 2400;
+ priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
+ lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
++#ifdef USE_PINPLUGIN
++ fskProtection = 0;
++#endif /* PINPLUGIN */
+ *file = 0;
+ aux = NULL;
+ event = NULL;
+@@ -79,12 +85,18 @@ cTimer::cTimer(const cEvent *Event)
+ stop -= 2400;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
++#ifdef USE_PINPLUGIN
++ fskProtection = 0;
++#endif /* PINPLUGIN */
+ *file = 0;
+ const char *Title = Event->Title();
+ if (!isempty(Title))
+ Utf8Strn0Cpy(file, Event->Title(), sizeof(file));
+ aux = NULL;
+ event = NULL; // let SetEvent() be called to get a log message
++#ifdef USE_PINPLUGIN
++ cStatus::MsgTimerCreation(this, Event);
++#endif /* PINPLUGIN */
+ }
+
+ cTimer::cTimer(const cTimer &Timer)
+@@ -119,6 +131,9 @@ cTimer& cTimer::operator= (const cTimer
+ stop = Timer.stop;
+ priority = Timer.priority;
+ lifetime = Timer.lifetime;
++#ifdef USE_PINPLUGIN
++ fskProtection = Timer.fskProtection;
++#endif /* PINPLUGIN */
+ strncpy(file, Timer.file, sizeof(file));
+ free(aux);
+ aux = Timer.aux ? strdup(Timer.aux) : NULL;
+@@ -313,6 +328,9 @@ bool cTimer::Parse(const char *s)
+ result = false;
+ }
+ }
++#ifdef USE_PINPLUGIN
++ fskProtection = aux && strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>");
++#endif /* PINPLUGIN */
+ free(channelbuffer);
+ free(daybuffer);
+ free(filebuffer);
+@@ -559,6 +577,9 @@ void cTimer::SetRecording(bool Recording
+ SetFlags(tfRecording);
+ else
+ ClrFlags(tfRecording);
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgRecordingChange(NULL, scAdd);
++#endif /* STREAMDEVEXT */
+ isyslog("timer %s %s", *ToDescr(), recording ? "start" : "stop");
+ }
+
+@@ -622,6 +643,35 @@ void cTimer::OnOff(void)
+ Matches(); // refresh start and end time
+ }
+
++#ifdef USE_PINPLUGIN
++void cTimer::SetFskProtection(int aFlag)
++{
++ char* p;
++ char* tmp = 0;
++
++ fskProtection = aFlag;
++
++ if (fskProtection && (!aux || !strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // add protection info to aux
++
++ if (aux) { tmp = strdup(aux); free(aux); }
++ asprintf(&aux,"%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "");
++ }
++ else if (!fskProtection && aux && (p = strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // remove protection info to aux
++
++ asprintf(&tmp, "%.*s%s", p-aux, aux, p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>"));
++ free(aux);
++ aux = strdup(tmp);
++ }
++
++ if (tmp)
++ free(tmp);
++}
++#endif /* PINPLUGIN */
++
+ // --- cTimers ---------------------------------------------------------------
+
+ cTimers Timers;
+@@ -651,7 +701,11 @@ cTimer *cTimers::GetMatch(time_t t)
+ static int LastPending = -1;
+ cTimer *t0 = NULL;
+ for (cTimer *ti = First(); ti; ti = Next(ti)) {
++#ifdef USE_LIVEBUFFER
++ if (!ti->Recording() && (ti->Matches(t) || cLiveBufferManager::InLiveBuffer(ti))) {
++#else
+ if (!ti->Recording() && ti->Matches(t)) {
++#endif /* LIVEBUFFER */
+ if (ti->Pending()) {
+ if (ti->Index() > LastPending)
+ LastPending = ti->Index();
+@@ -752,7 +806,11 @@ void cTimers::DeleteExpired(void)
+ cTimer *ti = First();
+ while (ti) {
+ cTimer *next = Next(ti);
++#ifdef USE_LIVEBUFFER
++ if (ti->Expired() && (!cLiveBufferManager::InLiveBuffer(ti) || !ti->HasFlags(tfActive))) {
++#else
+ if (ti->Expired()) {
++#endif /* LIVEBUFFER */
+ isyslog("deleting timer %s", *ti->ToDescr());
+ Del(ti);
+ SetModified();
+diff -ruNp vdr-1.6.0-2/timers.h vdr-1.6.0-2-extensions/timers.h
+--- vdr-1.6.0-2/timers.h 2008-02-16 15:33:23.000000000 +0100
++++ vdr-1.6.0-2-extensions/timers.h 2009-04-09 20:48:27.000000000 +0200
+@@ -20,12 +20,18 @@ enum eTimerFlags { tfNone = 0x0000,
+ tfInstant = 0x0002,
+ tfVps = 0x0004,
+ tfRecording = 0x0008,
++#ifdef USE_LIVEBUFFER
++ tfhasLiveBuf= 0x0010,
++#endif /* LIVEBUFFER */
+ tfAll = 0xFFFF,
+ };
+ enum eTimerMatch { tmNone, tmPartial, tmFull };
+
+ class cTimer : public cListObject {
+ friend class cMenuEditTimer;
++#ifdef USE_LIVEBUFFER
++ friend class cLiveBufferControl;
++#endif /* LIVEBUFFER */
+ private:
+ mutable time_t startTime, stopTime;
+ time_t lastSetEvent;
+@@ -37,6 +43,9 @@ private:
+ int start;
+ int stop;
+ int priority;
++#ifdef USE_PINPLUGIN
++ int fskProtection;
++#endif /* PINPLUGIN */
+ int lifetime;
+ mutable char file[MaxFileName];
+ char *aux;
+@@ -58,6 +67,9 @@ public:
+ int Start(void) const { return start; }
+ int Stop(void) const { return stop; }
+ int Priority(void) const { return priority; }
++#ifdef USE_PINPLUGIN
++ int FskProtection(void) const { return fskProtection; }
++#endif /* PINPLUGIN */
+ int Lifetime(void) const { return lifetime; }
+ const char *File(void) const { return file; }
+ time_t FirstDay(void) const { return weekdays ? day : 0; }
+@@ -86,6 +98,9 @@ public:
+ void SetInVpsMargin(bool InVpsMargin);
+ void SetPriority(int Priority);
+ void SetFlags(uint Flags);
++#ifdef USE_PINPLUGIN
++ void SetFskProtection(int aFlag);
++#endif /* PINPLUGIN */
+ void ClrFlags(uint Flags);
+ void InvFlags(uint Flags);
+ bool HasFlags(uint Flags) const;
+diff -ruNp vdr-1.6.0-2/tinystr.c vdr-1.6.0-2-extensions/tinystr.c
+--- vdr-1.6.0-2/tinystr.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/tinystr.c 2009-04-09 20:48:27.000000000 +0200
+@@ -0,0 +1,301 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original file by Yves Berquin.
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include "tinyxml.h"
++
++#ifndef TIXML_USE_STL
++
++
++#include <stdlib.h>
++#include <string.h>
++#include <ctype.h>
++
++#include "tinystr.h"
++
++// TiXmlString constructor, based on a C string
++TiXmlString::TiXmlString (const char* instring)
++{
++ unsigned newlen;
++ char * newstring;
++
++ if (!instring)
++ {
++ allocated = 0;
++ cstring = NULL;
++ current_length = 0;
++ return;
++ }
++ newlen = strlen (instring) + 1;
++ newstring = new char [newlen];
++ memcpy (newstring, instring, newlen);
++ // strcpy (newstring, instring);
++ allocated = newlen;
++ cstring = newstring;
++ current_length = newlen - 1;
++}
++
++// TiXmlString copy constructor
++TiXmlString::TiXmlString (const TiXmlString& copy)
++{
++ unsigned newlen;
++ char * newstring;
++
++ // Prevent copy to self!
++ if ( &copy == this )
++ return;
++
++ if (! copy . allocated)
++ {
++ allocated = 0;
++ cstring = NULL;
++ current_length = 0;
++ return;
++ }
++ newlen = copy . length () + 1;
++ newstring = new char [newlen];
++ // strcpy (newstring, copy . cstring);
++ memcpy (newstring, copy . cstring, newlen);
++ allocated = newlen;
++ cstring = newstring;
++ current_length = newlen - 1;
++}
++
++// TiXmlString = operator. Safe when assign own content
++void TiXmlString ::operator = (const char * content)
++{
++ unsigned newlen;
++ char * newstring;
++
++ if (! content)
++ {
++ empty_it ();
++ return;
++ }
++ newlen = strlen (content) + 1;
++ newstring = new char [newlen];
++ // strcpy (newstring, content);
++ memcpy (newstring, content, newlen);
++ empty_it ();
++ allocated = newlen;
++ cstring = newstring;
++ current_length = newlen - 1;
++}
++
++// = operator. Safe when assign own content
++void TiXmlString ::operator = (const TiXmlString & copy)
++{
++ unsigned newlen;
++ char * newstring;
++
++ if (! copy . length ())
++ {
++ empty_it ();
++ return;
++ }
++ newlen = copy . length () + 1;
++ newstring = new char [newlen];
++ // strcpy (newstring, copy . c_str ());
++ memcpy (newstring, copy . c_str (), newlen);
++ empty_it ();
++ allocated = newlen;
++ cstring = newstring;
++ current_length = newlen - 1;
++}
++
++
++// append a const char * to an existing TiXmlString
++void TiXmlString::append( const char* str, int len )
++{
++ char * new_string;
++ unsigned new_alloc, new_size, size_suffix;
++
++ // don't use strlen - it can overrun the len passed in!
++ const char* p = str;
++ size_suffix = 0;
++
++ while ( *p && size_suffix < (unsigned)len )
++ {
++ ++p;
++ ++size_suffix;
++ }
++ if ( !size_suffix)
++ return;
++
++ new_size = length () + size_suffix + 1;
++ // check if we need to expand
++ if (new_size > allocated)
++ {
++ // compute new size
++ new_alloc = assign_new_size (new_size);
++
++ // allocate new buffer
++ new_string = new char [new_alloc];
++ new_string [0] = 0;
++
++ // copy the previous allocated buffer into this one
++ if (allocated && cstring)
++ // strcpy (new_string, cstring);
++ memcpy (new_string, cstring, length ());
++
++ // append the suffix. It does exist, otherwize we wouldn't be expanding
++ // strncat (new_string, str, len);
++ memcpy (new_string + length (),
++ str,
++ size_suffix);
++
++ // return previsously allocated buffer if any
++ if (allocated && cstring)
++ delete [] cstring;
++
++ // update member variables
++ cstring = new_string;
++ allocated = new_alloc;
++ }
++ else
++ {
++ // we know we can safely append the new string
++ // strncat (cstring, str, len);
++ memcpy (cstring + length (),
++ str,
++ size_suffix);
++ }
++ current_length = new_size - 1;
++ cstring [current_length] = 0;
++}
++
++
++// append a const char * to an existing TiXmlString
++void TiXmlString::append( const char * suffix )
++{
++ char * new_string;
++ unsigned new_alloc, new_size;
++
++ new_size = length () + strlen (suffix) + 1;
++ // check if we need to expand
++ if (new_size > allocated)
++ {
++ // compute new size
++ new_alloc = assign_new_size (new_size);
++
++ // allocate new buffer
++ new_string = new char [new_alloc];
++ new_string [0] = 0;
++
++ // copy the previous allocated buffer into this one
++ if (allocated && cstring)
++ memcpy (new_string, cstring, 1 + length ());
++ // strcpy (new_string, cstring);
++
++ // append the suffix. It does exist, otherwize we wouldn't be expanding
++ // strcat (new_string, suffix);
++ memcpy (new_string + length (),
++ suffix,
++ strlen (suffix) + 1);
++
++ // return previsously allocated buffer if any
++ if (allocated && cstring)
++ delete [] cstring;
++
++ // update member variables
++ cstring = new_string;
++ allocated = new_alloc;
++ }
++ else
++ {
++ // we know we can safely append the new string
++ // strcat (cstring, suffix);
++ memcpy (cstring + length (),
++ suffix,
++ strlen (suffix) + 1);
++ }
++ current_length = new_size - 1;
++}
++
++// Check for TiXmlString equuivalence
++//bool TiXmlString::operator == (const TiXmlString & compare) const
++//{
++// return (! strcmp (c_str (), compare . c_str ()));
++//}
++
++//unsigned TiXmlString::length () const
++//{
++// if (allocated)
++// // return strlen (cstring);
++// return current_length;
++// return 0;
++//}
++
++
++unsigned TiXmlString::find (char tofind, unsigned offset) const
++{
++ char * lookup;
++
++ if (offset >= length ())
++ return (unsigned) notfound;
++ for (lookup = cstring + offset; * lookup; lookup++)
++ if (* lookup == tofind)
++ return lookup - cstring;
++ return (unsigned) notfound;
++}
++
++
++bool TiXmlString::operator == (const TiXmlString & compare) const
++{
++ if ( allocated && compare.allocated )
++ {
++ assert( cstring );
++ assert( compare.cstring );
++ return ( strcmp( cstring, compare.cstring ) == 0 );
++ }
++ return false;
++}
++
++
++bool TiXmlString::operator < (const TiXmlString & compare) const
++{
++ if ( allocated && compare.allocated )
++ {
++ assert( cstring );
++ assert( compare.cstring );
++ return ( strcmp( cstring, compare.cstring ) > 0 );
++ }
++ return false;
++}
++
++
++bool TiXmlString::operator > (const TiXmlString & compare) const
++{
++ if ( allocated && compare.allocated )
++ {
++ assert( cstring );
++ assert( compare.cstring );
++ return ( strcmp( cstring, compare.cstring ) < 0 );
++ }
++ return false;
++}
++
++
++#endif // TIXML_USE_STL
++#endif /* SETUP */
+diff -ruNp vdr-1.6.0-2/tinystr.h vdr-1.6.0-2-extensions/tinystr.h
+--- vdr-1.6.0-2/tinystr.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/tinystr.h 2009-04-09 20:48:27.000000000 +0200
+@@ -0,0 +1,244 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original file by Yves Berquin.
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include "tinyxml.h"
++
++
++#ifndef TIXML_USE_STL
++
++#ifndef TIXML_STRING_INCLUDED
++#define TIXML_STRING_INCLUDED
++
++#ifdef _MSC_VER
++#pragma warning( disable : 4786 ) // Debugger truncating names.
++#endif
++
++#include <assert.h>
++
++/*
++ TiXmlString is an emulation of the std::string template.
++ Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
++ Only the member functions relevant to the TinyXML project have been implemented.
++ The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
++ a string and there's no more room, we allocate a buffer twice as big as we need.
++*/
++class TiXmlString
++{
++ public :
++ // TiXmlString constructor, based on a string
++ TiXmlString (const char * instring);
++
++ // TiXmlString empty constructor
++ TiXmlString ()
++ {
++ allocated = 0;
++ cstring = NULL;
++ current_length = 0;
++ }
++
++ // TiXmlString copy constructor
++ TiXmlString (const TiXmlString& copy);
++
++ // TiXmlString destructor
++ ~ TiXmlString ()
++ {
++ empty_it ();
++ }
++
++ // Convert a TiXmlString into a classical char *
++ const char * c_str () const
++ {
++ if (allocated)
++ return cstring;
++ return "";
++ }
++
++ // Return the length of a TiXmlString
++ unsigned length () const
++ {
++ return ( allocated ) ? current_length : 0;
++ }
++
++ // TiXmlString = operator
++ void operator = (const char * content);
++
++ // = operator
++ void operator = (const TiXmlString & copy);
++
++ // += operator. Maps to append
++ TiXmlString& operator += (const char * suffix)
++ {
++ append (suffix);
++ return *this;
++ }
++
++ // += operator. Maps to append
++ TiXmlString& operator += (char single)
++ {
++ append (single);
++ return *this;
++ }
++
++ // += operator. Maps to append
++ TiXmlString& operator += (TiXmlString & suffix)
++ {
++ append (suffix);
++ return *this;
++ }
++ bool operator == (const TiXmlString & compare) const;
++ bool operator < (const TiXmlString & compare) const;
++ bool operator > (const TiXmlString & compare) const;
++
++ // Checks if a TiXmlString is empty
++ bool empty () const
++ {
++ return length () ? false : true;
++ }
++
++ // single char extraction
++ const char& at (unsigned index) const
++ {
++ assert( index < length ());
++ return cstring [index];
++ }
++
++ // find a char in a string. Return TiXmlString::notfound if not found
++ unsigned find (char lookup) const
++ {
++ return find (lookup, 0);
++ }
++
++ // find a char in a string from an offset. Return TiXmlString::notfound if not found
++ unsigned find (char tofind, unsigned offset) const;
++
++ /* Function to reserve a big amount of data when we know we'll need it. Be aware that this
++ function clears the content of the TiXmlString if any exists.
++ */
++ void reserve (unsigned size)
++ {
++ empty_it ();
++ if (size)
++ {
++ allocated = size;
++ cstring = new char [size];
++ cstring [0] = 0;
++ current_length = 0;
++ }
++ }
++
++ // [] operator
++ char& operator [] (unsigned index) const
++ {
++ assert( index < length ());
++ return cstring [index];
++ }
++
++ // Error value for find primitive
++ enum { notfound = 0xffffffff,
++ npos = notfound };
++
++ void append (const char *str, int len );
++
++ protected :
++
++ // The base string
++ char * cstring;
++ // Number of chars allocated
++ unsigned allocated;
++ // Current string size
++ unsigned current_length;
++
++ // New size computation. It is simplistic right now : it returns twice the amount
++ // we need
++ unsigned assign_new_size (unsigned minimum_to_allocate)
++ {
++ return minimum_to_allocate * 2;
++ }
++
++ // Internal function that clears the content of a TiXmlString
++ void empty_it ()
++ {
++ if (cstring)
++ delete [] cstring;
++ cstring = NULL;
++ allocated = 0;
++ current_length = 0;
++ }
++
++ void append (const char *suffix );
++
++ // append function for another TiXmlString
++ void append (const TiXmlString & suffix)
++ {
++ append (suffix . c_str ());
++ }
++
++ // append for a single char.
++ void append (char single)
++ {
++ if ( cstring && current_length < (allocated-1) )
++ {
++ cstring[ current_length ] = single;
++ ++current_length;
++ cstring[ current_length ] = 0;
++ }
++ else
++ {
++ char smallstr [2];
++ smallstr [0] = single;
++ smallstr [1] = 0;
++ append (smallstr);
++ }
++ }
++
++} ;
++
++/*
++ TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
++ Only the operators that we need for TinyXML have been developped.
++*/
++class TiXmlOutStream : public TiXmlString
++{
++public :
++ TiXmlOutStream () : TiXmlString () {}
++
++ // TiXmlOutStream << operator. Maps to TiXmlString::append
++ TiXmlOutStream & operator << (const char * in)
++ {
++ append (in);
++ return (* this);
++ }
++
++ // TiXmlOutStream << operator. Maps to TiXmlString::append
++ TiXmlOutStream & operator << (const TiXmlString & in)
++ {
++ append (in . c_str ());
++ return (* this);
++ }
++} ;
++
++#endif // TIXML_STRING_INCLUDED
++#endif // TIXML_USE_STL
++#endif /* SETUP */
+diff -ruNp vdr-1.6.0-2/tinyxml.c vdr-1.6.0-2-extensions/tinyxml.c
+--- vdr-1.6.0-2/tinyxml.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/tinyxml.c 2009-04-09 20:48:27.000000000 +0200
+@@ -0,0 +1,1429 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include <ctype.h>
++#include "tinyxml.h"
++
++#ifdef TIXML_USE_STL
++#include <sstream>
++#endif
++
++
++bool TiXmlBase::condenseWhiteSpace = true;
++
++void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_OSTREAM* stream )
++{
++ TIXML_STRING buffer;
++ PutString( str, &buffer );
++ (*stream) << buffer;
++}
++
++void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_STRING* outString )
++{
++ int i=0;
++
++ while( i<(int)str.length() )
++ {
++ unsigned char c = (unsigned char) str[i];
++
++ if ( c == '&'
++ && i < ( (int)str.length() - 2 )
++ && str[i+1] == '#'
++ && str[i+2] == 'x' )
++ {
++ // Hexadecimal character reference.
++ // Pass through unchanged.
++ // &#xA9; -- copyright symbol, for example.
++ //
++ // The -1 is a bug fix from Rob Laveaux. It keeps
++ // an overflow from happening if there is no ';'.
++ // There are actually 2 ways to exit this loop -
++ // while fails (error case) and break (semicolon found).
++ // However, there is no mechanism (currently) for
++ // this function to return an error.
++ while ( i<(int)str.length()-1 )
++ {
++ outString->append( str.c_str() + i, 1 );
++ ++i;
++ if ( str[i] == ';' )
++ break;
++ }
++ }
++ else if ( c == '&' )
++ {
++ outString->append( entity[0].str, entity[0].strLength );
++ ++i;
++ }
++ else if ( c == '<' )
++ {
++ outString->append( entity[1].str, entity[1].strLength );
++ ++i;
++ }
++ else if ( c == '>' )
++ {
++ outString->append( entity[2].str, entity[2].strLength );
++ ++i;
++ }
++ else if ( c == '\"' )
++ {
++ outString->append( entity[3].str, entity[3].strLength );
++ ++i;
++ }
++ else if ( c == '\'' )
++ {
++ outString->append( entity[4].str, entity[4].strLength );
++ ++i;
++ }
++ else if ( c < 32 )
++ {
++ // Easy pass at non-alpha/numeric/symbol
++ // Below 32 is symbolic.
++ char buf[ 32 ];
++ sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) );
++ outString->append( buf, strlen( buf ) );
++ ++i;
++ }
++ else
++ {
++ //char realc = (char) c;
++ //outString->append( &realc, 1 );
++ *outString += (char) c; // somewhat more efficient function call.
++ ++i;
++ }
++ }
++}
++
++
++// <-- Strange class for a bug fix. Search for STL_STRING_BUG
++TiXmlBase::StringToBuffer::StringToBuffer( const TIXML_STRING& str )
++{
++ buffer = new char[ str.length()+1 ];
++ if ( buffer )
++ {
++ strcpy( buffer, str.c_str() );
++ }
++}
++
++
++TiXmlBase::StringToBuffer::~StringToBuffer()
++{
++ delete [] buffer;
++}
++// End strange bug fix. -->
++
++
++TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase()
++{
++ parent = 0;
++ type = _type;
++ firstChild = 0;
++ lastChild = 0;
++ prev = 0;
++ next = 0;
++}
++
++
++TiXmlNode::~TiXmlNode()
++{
++ TiXmlNode* node = firstChild;
++ TiXmlNode* temp = 0;
++
++ while ( node )
++ {
++ temp = node;
++ node = node->next;
++ delete temp;
++ }
++}
++
++
++void TiXmlNode::CopyTo( TiXmlNode* target ) const
++{
++ target->SetValue (value.c_str() );
++ target->userData = userData;
++}
++
++
++void TiXmlNode::Clear()
++{
++ TiXmlNode* node = firstChild;
++ TiXmlNode* temp = 0;
++
++ while ( node )
++ {
++ temp = node;
++ node = node->next;
++ delete temp;
++ }
++
++ firstChild = 0;
++ lastChild = 0;
++}
++
++
++TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
++{
++ node->parent = this;
++
++ node->prev = lastChild;
++ node->next = 0;
++
++ if ( lastChild )
++ lastChild->next = node;
++ else
++ firstChild = node; // it was an empty list.
++
++ lastChild = node;
++ return node;
++}
++
++
++TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis )
++{
++ TiXmlNode* node = addThis.Clone();
++ if ( !node )
++ return 0;
++
++ return LinkEndChild( node );
++}
++
++
++TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis )
++{
++ if ( !beforeThis || beforeThis->parent != this )
++ return 0;
++
++ TiXmlNode* node = addThis.Clone();
++ if ( !node )
++ return 0;
++ node->parent = this;
++
++ node->next = beforeThis;
++ node->prev = beforeThis->prev;
++ if ( beforeThis->prev )
++ {
++ beforeThis->prev->next = node;
++ }
++ else
++ {
++ assert( firstChild == beforeThis );
++ firstChild = node;
++ }
++ beforeThis->prev = node;
++ return node;
++}
++
++
++TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis )
++{
++ if ( !afterThis || afterThis->parent != this )
++ return 0;
++
++ TiXmlNode* node = addThis.Clone();
++ if ( !node )
++ return 0;
++ node->parent = this;
++
++ node->prev = afterThis;
++ node->next = afterThis->next;
++ if ( afterThis->next )
++ {
++ afterThis->next->prev = node;
++ }
++ else
++ {
++ assert( lastChild == afterThis );
++ lastChild = node;
++ }
++ afterThis->next = node;
++ return node;
++}
++
++
++TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )
++{
++ if ( replaceThis->parent != this )
++ return 0;
++
++ TiXmlNode* node = withThis.Clone();
++ if ( !node )
++ return 0;
++
++ node->next = replaceThis->next;
++ node->prev = replaceThis->prev;
++
++ if ( replaceThis->next )
++ replaceThis->next->prev = node;
++ else
++ lastChild = node;
++
++ if ( replaceThis->prev )
++ replaceThis->prev->next = node;
++ else
++ firstChild = node;
++
++ delete replaceThis;
++ node->parent = this;
++ return node;
++}
++
++
++bool TiXmlNode::RemoveChild( TiXmlNode* removeThis )
++{
++ if ( removeThis->parent != this )
++ {
++ assert( 0 );
++ return false;
++ }
++
++ if ( removeThis->next )
++ removeThis->next->prev = removeThis->prev;
++ else
++ lastChild = removeThis->prev;
++
++ if ( removeThis->prev )
++ removeThis->prev->next = removeThis->next;
++ else
++ firstChild = removeThis->next;
++
++ delete removeThis;
++ return true;
++}
++
++TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const
++{
++ TiXmlNode* node;
++ for ( node = firstChild; node; node = node->next )
++ {
++ if ( node->SValue() == TIXML_STRING( _value ))
++ return node;
++ }
++ return 0;
++}
++
++TiXmlNode* TiXmlNode::LastChild( const char * _value ) const
++{
++ TiXmlNode* node;
++ for ( node = lastChild; node; node = node->prev )
++ {
++ if ( node->SValue() == TIXML_STRING (_value))
++ return node;
++ }
++ return 0;
++}
++
++TiXmlNode* TiXmlNode::IterateChildren( TiXmlNode* previous ) const
++{
++ if ( !previous )
++ {
++ return FirstChild();
++ }
++ else
++ {
++ assert( previous->parent == this );
++ return previous->NextSibling();
++ }
++}
++
++TiXmlNode* TiXmlNode::IterateChildren( const char * val, TiXmlNode* previous ) const
++{
++ if ( !previous )
++ {
++ return FirstChild( val );
++ }
++ else
++ {
++ assert( previous->parent == this );
++ return previous->NextSibling( val );
++ }
++}
++
++TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const
++{
++ TiXmlNode* node;
++ for ( node = next; node; node = node->next )
++ {
++ if ( node->SValue() == TIXML_STRING (_value))
++ return node;
++ }
++ return 0;
++}
++
++
++TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const
++{
++ TiXmlNode* node;
++ for ( node = prev; node; node = node->prev )
++ {
++ if ( node->SValue() == TIXML_STRING (_value))
++ return node;
++ }
++ return 0;
++}
++
++void TiXmlElement::RemoveAttribute( const char * name )
++{
++ TiXmlAttribute* node = attributeSet.Find( name );
++ if ( node )
++ {
++ attributeSet.Remove( node );
++ delete node;
++ }
++}
++
++TiXmlElement* TiXmlNode::FirstChildElement() const
++{
++ TiXmlNode* node;
++
++ for ( node = FirstChild();
++ node;
++ node = node->NextSibling() )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const
++{
++ TiXmlNode* node;
++
++ for ( node = FirstChild( _value );
++ node;
++ node = node->NextSibling( _value ) )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++
++TiXmlElement* TiXmlNode::NextSiblingElement() const
++{
++ TiXmlNode* node;
++
++ for ( node = NextSibling();
++ node;
++ node = node->NextSibling() )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const
++{
++ TiXmlNode* node;
++
++ for ( node = NextSibling( _value );
++ node;
++ node = node->NextSibling( _value ) )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++
++
++TiXmlDocument* TiXmlNode::GetDocument() const
++{
++ const TiXmlNode* node;
++
++ for( node = this; node; node = node->parent )
++ {
++ if ( node->ToDocument() )
++ return node->ToDocument();
++ }
++ return 0;
++}
++
++
++TiXmlElement::TiXmlElement (const char * _value)
++ : TiXmlNode( TiXmlNode::ELEMENT )
++{
++ firstChild = lastChild = 0;
++ value = _value;
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlElement::TiXmlElement( const std::string& _value )
++ : TiXmlNode( TiXmlNode::ELEMENT )
++{
++ firstChild = lastChild = 0;
++ value = _value;
++}
++#endif
++
++
++TiXmlElement::TiXmlElement( const TiXmlElement& copy)
++ : TiXmlNode( TiXmlNode::ELEMENT )
++{
++ firstChild = lastChild = 0;
++ copy.CopyTo( this );
++}
++
++
++void TiXmlElement::operator=( const TiXmlElement& base )
++{
++ ClearThis();
++ base.CopyTo( this );
++}
++
++
++TiXmlElement::~TiXmlElement()
++{
++ ClearThis();
++}
++
++
++void TiXmlElement::ClearThis()
++{
++ Clear();
++ while( attributeSet.First() )
++ {
++ TiXmlAttribute* node = attributeSet.First();
++ attributeSet.Remove( node );
++ delete node;
++ }
++}
++
++
++const char * TiXmlElement::Attribute( const char * name ) const
++{
++ TiXmlAttribute* node = attributeSet.Find( name );
++
++ if ( node )
++ return node->Value();
++
++ return 0;
++}
++
++
++const char * TiXmlElement::Attribute( const char * name, int* i ) const
++{
++ const char * s = Attribute( name );
++ if ( i )
++ {
++ if ( s )
++ *i = atoi( s );
++ else
++ *i = 0;
++ }
++ return s;
++}
++
++
++const char * TiXmlElement::Attribute( const char * name, double* d ) const
++{
++ const char * s = Attribute( name );
++ if ( d )
++ {
++ if ( s )
++ *d = atof( s );
++ else
++ *d = 0;
++ }
++ return s;
++}
++
++
++int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const
++{
++ TiXmlAttribute* node = attributeSet.Find( name );
++ if ( !node )
++ return TIXML_NO_ATTRIBUTE;
++
++ return node->QueryIntValue( ival );
++}
++
++
++int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const
++{
++ TiXmlAttribute* node = attributeSet.Find( name );
++ if ( !node )
++ return TIXML_NO_ATTRIBUTE;
++
++ return node->QueryDoubleValue( dval );
++}
++
++
++void TiXmlElement::SetAttribute( const char * name, int val )
++{
++ char buf[64];
++ sprintf( buf, "%d", val );
++ SetAttribute( name, buf );
++}
++
++
++void TiXmlElement::SetDoubleAttribute( const char * name, double val )
++{
++ char buf[128];
++ sprintf( buf, "%f", val );
++ SetAttribute( name, buf );
++}
++
++
++void TiXmlElement::SetAttribute( const char * name, const char * _value )
++{
++ TiXmlAttribute* node = attributeSet.Find( name );
++ if ( node )
++ {
++ node->SetValue( _value );
++ return;
++ }
++
++ TiXmlAttribute* attrib = new TiXmlAttribute( name, _value );
++ if ( attrib )
++ {
++ attributeSet.Add( attrib );
++ }
++ else
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ }
++}
++
++void TiXmlElement::Print( FILE* cfile, int depth ) const
++{
++ int i;
++ for ( i=0; i<depth; i++ )
++ {
++ fprintf( cfile, " " );
++ }
++
++ fprintf( cfile, "<%s", value.c_str() );
++
++ TiXmlAttribute* attrib;
++ for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
++ {
++ fprintf( cfile, " " );
++ attrib->Print( cfile, depth );
++ }
++
++ // There are 3 different formatting approaches:
++ // 1) An element without children is printed as a <foo /> node
++ // 2) An element with only a text child is printed as <foo> text </foo>
++ // 3) An element with children is printed on multiple lines.
++ TiXmlNode* node;
++ if ( !firstChild )
++ {
++ fprintf( cfile, " />" );
++ }
++ else if ( firstChild == lastChild && firstChild->ToText() )
++ {
++ fprintf( cfile, ">" );
++ firstChild->Print( cfile, depth + 1 );
++ fprintf( cfile, "</%s>", value.c_str() );
++ }
++ else
++ {
++ fprintf( cfile, ">" );
++
++ for ( node = firstChild; node; node=node->NextSibling() )
++ {
++ if ( !node->ToText() )
++ {
++ fprintf( cfile, "\n" );
++ }
++ node->Print( cfile, depth+1 );
++ }
++ fprintf( cfile, "\n" );
++ for( i=0; i<depth; ++i )
++ fprintf( cfile, " " );
++ fprintf( cfile, "</%s>", value.c_str() );
++ }
++}
++
++void TiXmlElement::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ (*stream) << "<" << value;
++
++ TiXmlAttribute* attrib;
++ for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
++ {
++ (*stream) << " ";
++ attrib->StreamOut( stream );
++ }
++
++ // If this node has children, give it a closing tag. Else
++ // make it an empty tag.
++ TiXmlNode* node;
++ if ( firstChild )
++ {
++ (*stream) << ">";
++
++ for ( node = firstChild; node; node=node->NextSibling() )
++ {
++ node->StreamOut( stream );
++ }
++ (*stream) << "</" << value << ">";
++ }
++ else
++ {
++ (*stream) << " />";
++ }
++}
++
++
++void TiXmlElement::CopyTo( TiXmlElement* target ) const
++{
++ // superclass:
++ TiXmlNode::CopyTo( target );
++
++ // Element class:
++ // Clone the attributes, then clone the children.
++ TiXmlAttribute* attribute = 0;
++ for( attribute = attributeSet.First();
++ attribute;
++ attribute = attribute->Next() )
++ {
++ target->SetAttribute( attribute->Name(), attribute->Value() );
++ }
++
++ TiXmlNode* node = 0;
++ for ( node = firstChild; node; node = node->NextSibling() )
++ {
++ target->LinkEndChild( node->Clone() );
++ }
++}
++
++
++TiXmlNode* TiXmlElement::Clone() const
++{
++ TiXmlElement* clone = new TiXmlElement( Value() );
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++ tabsize = 4;
++ ClearError();
++}
++
++TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++ tabsize = 4;
++ value = documentName;
++ ClearError();
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++ tabsize = 4;
++ value = documentName;
++ ClearError();
++}
++#endif
++
++
++TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++ copy.CopyTo( this );
++}
++
++
++void TiXmlDocument::operator=( const TiXmlDocument& copy )
++{
++ Clear();
++ copy.CopyTo( this );
++}
++
++
++bool TiXmlDocument::LoadFile( TiXmlEncoding encoding )
++{
++ // See STL_STRING_BUG below.
++ StringToBuffer buf( value );
++
++ if ( buf.buffer && LoadFile( buf.buffer, encoding ) )
++ return true;
++
++ return false;
++}
++
++
++bool TiXmlDocument::SaveFile() const
++{
++ // See STL_STRING_BUG below.
++ StringToBuffer buf( value );
++
++ if ( buf.buffer && SaveFile( buf.buffer ) )
++ return true;
++
++ return false;
++}
++
++bool TiXmlDocument::LoadFile( const char* filename, TiXmlEncoding encoding )
++{
++ // Delete the existing data:
++ Clear();
++ location.Clear();
++
++ // There was a really terrifying little bug here. The code:
++ // value = filename
++ // in the STL case, cause the assignment method of the std::string to
++ // be called. What is strange, is that the std::string had the same
++ // address as it's c_str() method, and so bad things happen. Looks
++ // like a bug in the Microsoft STL implementation.
++ // See STL_STRING_BUG above.
++ // Fixed with the StringToBuffer class.
++ value = filename;
++
++ FILE* file = fopen( value.c_str (), "r" );
++
++ if ( file )
++ {
++ // Get the file size, so we can pre-allocate the string. HUGE speed impact.
++ long length = 0;
++ fseek( file, 0, SEEK_END );
++ length = ftell( file );
++ fseek( file, 0, SEEK_SET );
++
++ // Strange case, but good to handle up front.
++ if ( length == 0 )
++ {
++ fclose( file );
++ return false;
++ }
++
++ // If we have a file, assume it is all one big XML file, and read it in.
++ // The document parser may decide the document ends sooner than the entire file, however.
++ TIXML_STRING data;
++ data.reserve( length );
++
++ const int BUF_SIZE = 2048;
++ char buf[BUF_SIZE];
++
++ while( fgets( buf, BUF_SIZE, file ) )
++ {
++ data += buf;
++ }
++ fclose( file );
++
++ Parse( data.c_str(), 0, encoding );
++
++ if ( Error() )
++ return false;
++ else
++ return true;
++ }
++ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return false;
++}
++
++bool TiXmlDocument::SaveFile( const char * filename ) const
++{
++ // The old c stuff lives on...
++ FILE* fp = fopen( filename, "w" );
++ if ( fp )
++ {
++ Print( fp, 0 );
++ fclose( fp );
++ return true;
++ }
++ return false;
++}
++
++
++void TiXmlDocument::CopyTo( TiXmlDocument* target ) const
++{
++ TiXmlNode::CopyTo( target );
++
++ target->error = error;
++ target->errorDesc = errorDesc.c_str ();
++
++ TiXmlNode* node = 0;
++ for ( node = firstChild; node; node = node->NextSibling() )
++ {
++ target->LinkEndChild( node->Clone() );
++ }
++}
++
++
++TiXmlNode* TiXmlDocument::Clone() const
++{
++ TiXmlDocument* clone = new TiXmlDocument();
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++void TiXmlDocument::Print( FILE* cfile, int depth ) const
++{
++ TiXmlNode* node;
++ for ( node=FirstChild(); node; node=node->NextSibling() )
++ {
++ node->Print( cfile, depth );
++ fprintf( cfile, "\n" );
++ }
++}
++
++void TiXmlDocument::StreamOut( TIXML_OSTREAM * out ) const
++{
++ TiXmlNode* node;
++ for ( node=FirstChild(); node; node=node->NextSibling() )
++ {
++ node->StreamOut( out );
++
++ // Special rule for streams: stop after the root element.
++ // The stream in code will only read one element, so don't
++ // write more than one.
++ if ( node->ToElement() )
++ break;
++ }
++}
++
++
++TiXmlAttribute* TiXmlAttribute::Next() const
++{
++ // We are using knowledge of the sentinel. The sentinel
++ // have a value or name.
++ if ( next->value.empty() && next->name.empty() )
++ return 0;
++ return next;
++}
++
++
++TiXmlAttribute* TiXmlAttribute::Previous() const
++{
++ // We are using knowledge of the sentinel. The sentinel
++ // have a value or name.
++ if ( prev->value.empty() && prev->name.empty() )
++ return 0;
++ return prev;
++}
++
++
++void TiXmlAttribute::Print( FILE* cfile, int /*depth*/ ) const
++{
++ TIXML_STRING n, v;
++
++ PutString( name, &n );
++ PutString( value, &v );
++
++ if (value.find ('\"') == TIXML_STRING::npos)
++ fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() );
++ else
++ fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() );
++}
++
++
++void TiXmlAttribute::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ if (value.find( '\"' ) != TIXML_STRING::npos)
++ {
++ PutString( name, stream );
++ (*stream) << "=" << "'";
++ PutString( value, stream );
++ (*stream) << "'";
++ }
++ else
++ {
++ PutString( name, stream );
++ (*stream) << "=" << "\"";
++ PutString( value, stream );
++ (*stream) << "\"";
++ }
++}
++
++int TiXmlAttribute::QueryIntValue( int* ival ) const
++{
++ if ( sscanf( value.c_str(), "%d", ival ) == 1 )
++ return TIXML_SUCCESS;
++ return TIXML_WRONG_TYPE;
++}
++
++int TiXmlAttribute::QueryDoubleValue( double* dval ) const
++{
++ if ( sscanf( value.c_str(), "%lf", dval ) == 1 )
++ return TIXML_SUCCESS;
++ return TIXML_WRONG_TYPE;
++}
++
++void TiXmlAttribute::SetIntValue( int _value )
++{
++ char buf [64];
++ sprintf (buf, "%d", _value);
++ SetValue (buf);
++}
++
++void TiXmlAttribute::SetDoubleValue( double _value )
++{
++ char buf [64];
++ sprintf (buf, "%lf", _value);
++ SetValue (buf);
++}
++
++const int TiXmlAttribute::IntValue() const
++{
++ return atoi (value.c_str ());
++}
++
++const double TiXmlAttribute::DoubleValue() const
++{
++ return atof (value.c_str ());
++}
++
++
++TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT )
++{
++ copy.CopyTo( this );
++}
++
++
++void TiXmlComment::operator=( const TiXmlComment& base )
++{
++ Clear();
++ base.CopyTo( this );
++}
++
++
++void TiXmlComment::Print( FILE* cfile, int depth ) const
++{
++ for ( int i=0; i<depth; i++ )
++ {
++ fputs( " ", cfile );
++ }
++ fprintf( cfile, "<!--%s-->", value.c_str() );
++}
++
++void TiXmlComment::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ (*stream) << "<!--";
++ //PutString( value, stream );
++ (*stream) << value;
++ (*stream) << "-->";
++}
++
++
++void TiXmlComment::CopyTo( TiXmlComment* target ) const
++{
++ TiXmlNode::CopyTo( target );
++}
++
++
++TiXmlNode* TiXmlComment::Clone() const
++{
++ TiXmlComment* clone = new TiXmlComment();
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++void TiXmlText::Print( FILE* cfile, int /*depth*/ ) const
++{
++ TIXML_STRING buffer;
++ PutString( value, &buffer );
++ fprintf( cfile, "%s", buffer.c_str() );
++}
++
++
++void TiXmlText::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ PutString( value, stream );
++}
++
++
++void TiXmlText::CopyTo( TiXmlText* target ) const
++{
++ TiXmlNode::CopyTo( target );
++}
++
++
++TiXmlNode* TiXmlText::Clone() const
++{
++ TiXmlText* clone = 0;
++ clone = new TiXmlText( "" );
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++TiXmlDeclaration::TiXmlDeclaration( const char * _version,
++ const char * _encoding,
++ const char * _standalone )
++ : TiXmlNode( TiXmlNode::DECLARATION )
++{
++ version = _version;
++ encoding = _encoding;
++ standalone = _standalone;
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
++ const std::string& _encoding,
++ const std::string& _standalone )
++ : TiXmlNode( TiXmlNode::DECLARATION )
++{
++ version = _version;
++ encoding = _encoding;
++ standalone = _standalone;
++}
++#endif
++
++
++TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )
++ : TiXmlNode( TiXmlNode::DECLARATION )
++{
++ copy.CopyTo( this );
++}
++
++
++void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy )
++{
++ Clear();
++ copy.CopyTo( this );
++}
++
++
++void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/ ) const
++{
++ fprintf (cfile, "<?xml ");
++
++ if ( !version.empty() )
++ fprintf (cfile, "version=\"%s\" ", version.c_str ());
++ if ( !encoding.empty() )
++ fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ());
++ if ( !standalone.empty() )
++ fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ());
++ fprintf (cfile, "?>");
++}
++
++void TiXmlDeclaration::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ (*stream) << "<?xml ";
++
++ if ( !version.empty() )
++ {
++ (*stream) << "version=\"";
++ PutString( version, stream );
++ (*stream) << "\" ";
++ }
++ if ( !encoding.empty() )
++ {
++ (*stream) << "encoding=\"";
++ PutString( encoding, stream );
++ (*stream ) << "\" ";
++ }
++ if ( !standalone.empty() )
++ {
++ (*stream) << "standalone=\"";
++ PutString( standalone, stream );
++ (*stream) << "\" ";
++ }
++ (*stream) << "?>";
++}
++
++
++void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const
++{
++ TiXmlNode::CopyTo( target );
++
++ target->version = version;
++ target->encoding = encoding;
++ target->standalone = standalone;
++}
++
++
++TiXmlNode* TiXmlDeclaration::Clone() const
++{
++ TiXmlDeclaration* clone = new TiXmlDeclaration();
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++void TiXmlUnknown::Print( FILE* cfile, int depth ) const
++{
++ for ( int i=0; i<depth; i++ )
++ fprintf( cfile, " " );
++ fprintf( cfile, "<%s>", value.c_str() );
++}
++
++
++void TiXmlUnknown::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ (*stream) << "<" << value << ">"; // Don't use entities here! It is unknown.
++}
++
++
++void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const
++{
++ TiXmlNode::CopyTo( target );
++}
++
++
++TiXmlNode* TiXmlUnknown::Clone() const
++{
++ TiXmlUnknown* clone = new TiXmlUnknown();
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++TiXmlAttributeSet::TiXmlAttributeSet()
++{
++ sentinel.next = &sentinel;
++ sentinel.prev = &sentinel;
++}
++
++
++TiXmlAttributeSet::~TiXmlAttributeSet()
++{
++ assert( sentinel.next == &sentinel );
++ assert( sentinel.prev == &sentinel );
++}
++
++
++void TiXmlAttributeSet::Add( TiXmlAttribute* addMe )
++{
++ assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set.
++
++ addMe->next = &sentinel;
++ addMe->prev = sentinel.prev;
++
++ sentinel.prev->next = addMe;
++ sentinel.prev = addMe;
++}
++
++void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
++{
++ TiXmlAttribute* node;
++
++ for( node = sentinel.next; node != &sentinel; node = node->next )
++ {
++ if ( node == removeMe )
++ {
++ node->prev->next = node->next;
++ node->next->prev = node->prev;
++ node->next = 0;
++ node->prev = 0;
++ return;
++ }
++ }
++ assert( 0 ); // we tried to remove a non-linked attribute.
++}
++
++TiXmlAttribute* TiXmlAttributeSet::Find( const char * name ) const
++{
++ TiXmlAttribute* node;
++
++ for( node = sentinel.next; node != &sentinel; node = node->next )
++ {
++ if ( node->name == name )
++ return node;
++ }
++ return 0;
++}
++
++
++#ifdef TIXML_USE_STL
++TIXML_ISTREAM & operator >> (TIXML_ISTREAM & in, TiXmlNode & base)
++{
++ TIXML_STRING tag;
++ tag.reserve( 8 * 1000 );
++ base.StreamIn( &in, &tag );
++
++ base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING );
++ return in;
++}
++#endif
++
++
++TIXML_OSTREAM & operator<< (TIXML_OSTREAM & out, const TiXmlNode & base)
++{
++ base.StreamOut (& out);
++ return out;
++}
++
++
++#ifdef TIXML_USE_STL
++std::string & operator<< (std::string& out, const TiXmlNode& base )
++{
++ std::ostringstream os_stream( std::ostringstream::out );
++ base.StreamOut( &os_stream );
++
++ out.append( os_stream.str() );
++ return out;
++}
++#endif
++
++
++TiXmlHandle TiXmlHandle::FirstChild() const
++{
++ if ( node )
++ {
++ TiXmlNode* child = node->FirstChild();
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const
++{
++ if ( node )
++ {
++ TiXmlNode* child = node->FirstChild( value );
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChildElement() const
++{
++ if ( node )
++ {
++ TiXmlElement* child = node->FirstChildElement();
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const
++{
++ if ( node )
++ {
++ TiXmlElement* child = node->FirstChildElement( value );
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::Child( int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlNode* child = node->FirstChild();
++ for ( i=0;
++ child && i<count;
++ child = child->NextSibling(), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlNode* child = node->FirstChild( value );
++ for ( i=0;
++ child && i<count;
++ child = child->NextSibling( value ), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::ChildElement( int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlElement* child = node->FirstChildElement();
++ for ( i=0;
++ child && i<count;
++ child = child->NextSiblingElement(), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlElement* child = node->FirstChildElement( value );
++ for ( i=0;
++ child && i<count;
++ child = child->NextSiblingElement( value ), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++#endif /* SETUP */
+diff -ruNp vdr-1.6.0-2/tinyxmlerror.c vdr-1.6.0-2-extensions/tinyxmlerror.c
+--- vdr-1.6.0-2/tinyxmlerror.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/tinyxmlerror.c 2009-04-09 20:48:27.000000000 +0200
+@@ -0,0 +1,53 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include "tinyxml.h"
++
++// The goal of the seperate error file is to make the first
++// step towards localization. tinyxml (currently) only supports
++// latin-1, but at least the error messages could now be translated.
++//
++// It also cleans up the code a bit.
++//
++
++const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] =
++{
++ "No error",
++ "Error",
++ "Failed to open file",
++ "Memory allocation failed.",
++ "Error parsing Element.",
++ "Failed to read Element name",
++ "Error reading Element value.",
++ "Error reading Attributes.",
++ "Error: empty tag.",
++ "Error reading end tag.",
++ "Error parsing Unknown.",
++ "Error parsing Comment.",
++ "Error parsing Declaration.",
++ "Error document empty.",
++ "Error null (0) or unexpected EOF found in input stream.",
++};
++#endif /* SETUP */
+diff -ruNp vdr-1.6.0-2/tinyxml.h vdr-1.6.0-2-extensions/tinyxml.h
+--- vdr-1.6.0-2/tinyxml.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/tinyxml.h 2009-04-09 20:48:27.000000000 +0200
+@@ -0,0 +1,1372 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++
++#ifndef TINYXML_INCLUDED
++#define TINYXML_INCLUDED
++
++#ifdef _MSC_VER
++#pragma warning( disable : 4530 )
++#pragma warning( disable : 4786 )
++#endif
++
++#include <ctype.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <assert.h>
++
++// Help out windows:
++#if defined( _DEBUG ) && !defined( DEBUG )
++#define DEBUG
++#endif
++
++#if defined( DEBUG ) && defined( _MSC_VER )
++#include <windows.h>
++#define TIXML_LOG OutputDebugString
++#else
++#define TIXML_LOG printf
++#endif
++
++#ifdef TIXML_USE_STL
++ #include <string>
++ #include <iostream>
++ #define TIXML_STRING std::string
++ #define TIXML_ISTREAM std::istream
++ #define TIXML_OSTREAM std::ostream
++#else
++ #include "tinystr.h"
++ #define TIXML_STRING TiXmlString
++ #define TIXML_OSTREAM TiXmlOutStream
++#endif
++
++class TiXmlDocument;
++class TiXmlElement;
++class TiXmlComment;
++class TiXmlUnknown;
++class TiXmlAttribute;
++class TiXmlText;
++class TiXmlDeclaration;
++class TiXmlParsingData;
++
++const int TIXML_MAJOR_VERSION = 2;
++const int TIXML_MINOR_VERSION = 3;
++const int TIXML_PATCH_VERSION = 2;
++
++/* Internal structure for tracking location of items
++ in the XML file.
++*/
++struct TiXmlCursor
++{
++ TiXmlCursor() { Clear(); }
++ void Clear() { row = col = -1; }
++
++ int row; // 0 based.
++ int col; // 0 based.
++};
++
++
++// Only used by Attribute::Query functions
++enum
++{
++ TIXML_SUCCESS,
++ TIXML_NO_ATTRIBUTE,
++ TIXML_WRONG_TYPE
++};
++
++
++// Used by the parsing routines.
++enum TiXmlEncoding
++{
++ TIXML_ENCODING_UNKNOWN,
++ TIXML_ENCODING_UTF8,
++ TIXML_ENCODING_LEGACY
++};
++
++const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
++
++/** TiXmlBase is a base class for every class in TinyXml.
++ It does little except to establish that TinyXml classes
++ can be printed and provide some utility functions.
++
++ In XML, the document and elements can contain
++ other elements and other types of nodes.
++
++ @verbatim
++ A Document can contain: Element (container or leaf)
++ Comment (leaf)
++ Unknown (leaf)
++ Declaration( leaf )
++
++ An Element can contain: Element (container or leaf)
++ Text (leaf)
++ Attributes (not on tree)
++ Comment (leaf)
++ Unknown (leaf)
++
++ A Decleration contains: Attributes (not on tree)
++ @endverbatim
++*/
++class TiXmlBase
++{
++ friend class TiXmlNode;
++ friend class TiXmlElement;
++ friend class TiXmlDocument;
++
++public:
++ TiXmlBase() : userData(0) {}
++ virtual ~TiXmlBase() {}
++
++ /** All TinyXml classes can print themselves to a filestream.
++ This is a formatted print, and will insert tabs and newlines.
++
++ (For an unformatted stream, use the << operator.)
++ */
++ virtual void Print( FILE* cfile, int depth ) const = 0;
++
++ /** The world does not agree on whether white space should be kept or
++ not. In order to make everyone happy, these global, static functions
++ are provided to set whether or not TinyXml will condense all white space
++ into a single space or not. The default is to condense. Note changing this
++ values is not thread safe.
++ */
++ static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; }
++
++ /// Return the current white space setting.
++ static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; }
++
++ /** Return the position, in the original source file, of this node or attribute.
++ The row and column are 1-based. (That is the first row and first column is
++ 1,1). If the returns values are 0 or less, then the parser does not have
++ a row and column value.
++
++ Generally, the row and column value will be set when the TiXmlDocument::Load(),
++ TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set
++ when the DOM was created from operator>>.
++
++ The values reflect the initial load. Once the DOM is modified programmatically
++ (by adding or changing nodes and attributes) the new values will NOT update to
++ reflect changes in the document.
++
++ There is a minor performance cost to computing the row and column. Computation
++ can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.
++
++ @sa TiXmlDocument::SetTabSize()
++ */
++ int Row() const { return location.row + 1; }
++ int Column() const { return location.col + 1; } ///< See Row()
++
++ void SetUserData( void* user ) { userData = user; }
++ void* GetUserData() { return userData; }
++
++ // Table that returs, for a given lead byte, the total number of bytes
++ // in the UTF-8 sequence.
++ static const int utf8ByteTable[256];
++
++ virtual const char* Parse( const char* p,
++ TiXmlParsingData* data,
++ TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0;
++
++protected:
++
++ // See STL_STRING_BUG
++ // Utility class to overcome a bug.
++ class StringToBuffer
++ {
++ public:
++ StringToBuffer( const TIXML_STRING& str );
++ ~StringToBuffer();
++ char* buffer;
++ };
++
++ static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );
++ inline static bool IsWhiteSpace( char c )
++ {
++ return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' );
++ }
++
++ virtual void StreamOut (TIXML_OSTREAM *) const = 0;
++
++ #ifdef TIXML_USE_STL
++ static bool StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ static bool StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag );
++ #endif
++
++ /* Reads an XML name into the string provided. Returns
++ a pointer just past the last character of the name,
++ or 0 if the function has an error.
++ */
++ static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding );
++
++ /* Reads text. Returns a pointer past the given end tag.
++ Wickedly complex options, but it keeps the (sensitive) code in one place.
++ */
++ static const char* ReadText( const char* in, // where to start
++ TIXML_STRING* text, // the string read
++ bool ignoreWhiteSpace, // whether to keep the white space
++ const char* endTag, // what ends this text
++ bool ignoreCase, // whether to ignore case in the end tag
++ TiXmlEncoding encoding ); // the current encoding
++
++ // If an entity has been found, transform it into a character.
++ static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding );
++
++ // Get a character, while interpreting entities.
++ // The length can be from 0 to 4 bytes.
++ inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding )
++ {
++ assert( p );
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ *length = utf8ByteTable[ *((unsigned char*)p) ];
++ assert( *length >= 0 && *length < 5 );
++ }
++ else
++ {
++ *length = 1;
++ }
++
++ if ( *length == 1 )
++ {
++ if ( *p == '&' )
++ return GetEntity( p, _value, length, encoding );
++ *_value = *p;
++ return p+1;
++ }
++ else if ( *length )
++ {
++ strncpy( _value, p, *length );
++ return p + (*length);
++ }
++ else
++ {
++ // Not valid text.
++ return 0;
++ }
++ }
++
++ // Puts a string to a stream, expanding entities as it goes.
++ // Note this should not contian the '<', '>', etc, or they will be transformed into entities!
++ static void PutString( const TIXML_STRING& str, TIXML_OSTREAM* out );
++
++ static void PutString( const TIXML_STRING& str, TIXML_STRING* out );
++
++ // Return true if the next characters in the stream are any of the endTag sequences.
++ // Ignore case only works for english, and should only be relied on when comparing
++ // to Engilish words: StringEqual( p, "version", true ) is fine.
++ static bool StringEqual( const char* p,
++ const char* endTag,
++ bool ignoreCase,
++ TiXmlEncoding encoding );
++
++
++ enum
++ {
++ TIXML_NO_ERROR = 0,
++ TIXML_ERROR,
++ TIXML_ERROR_OPENING_FILE,
++ TIXML_ERROR_OUT_OF_MEMORY,
++ TIXML_ERROR_PARSING_ELEMENT,
++ TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
++ TIXML_ERROR_READING_ELEMENT_VALUE,
++ TIXML_ERROR_READING_ATTRIBUTES,
++ TIXML_ERROR_PARSING_EMPTY,
++ TIXML_ERROR_READING_END_TAG,
++ TIXML_ERROR_PARSING_UNKNOWN,
++ TIXML_ERROR_PARSING_COMMENT,
++ TIXML_ERROR_PARSING_DECLARATION,
++ TIXML_ERROR_DOCUMENT_EMPTY,
++ TIXML_ERROR_EMBEDDED_NULL,
++
++ TIXML_ERROR_STRING_COUNT
++ };
++ static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
++
++ TiXmlCursor location;
++
++ /// Field containing a generic user pointer
++ void* userData;
++
++ // None of these methods are reliable for any language except English.
++ // Good for approximation, not great for accuracy.
++ static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding );
++ static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding );
++ inline static int ToLower( int v, TiXmlEncoding encoding )
++ {
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ if ( v < 128 ) return tolower( v );
++ return v;
++ }
++ else
++ {
++ return tolower( v );
++ }
++ }
++ static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
++
++private:
++ TiXmlBase( const TiXmlBase& ); // not implemented.
++ void operator=( const TiXmlBase& base ); // not allowed.
++
++ struct Entity
++ {
++ const char* str;
++ unsigned int strLength;
++ char chr;
++ };
++ enum
++ {
++ NUM_ENTITY = 5,
++ MAX_ENTITY_LENGTH = 6
++
++ };
++ static Entity entity[ NUM_ENTITY ];
++ static bool condenseWhiteSpace;
++};
++
++
++/** The parent class for everything in the Document Object Model.
++ (Except for attributes).
++ Nodes have siblings, a parent, and children. A node can be
++ in a document, or stand on its own. The type of a TiXmlNode
++ can be queried, and it can be cast to its more defined type.
++*/
++class TiXmlNode : public TiXmlBase
++{
++ friend class TiXmlDocument;
++ friend class TiXmlElement;
++
++public:
++ #ifdef TIXML_USE_STL
++
++ /** An input stream operator, for every class. Tolerant of newlines and
++ formatting, but doesn't expect them.
++ */
++ friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
++
++ /** An output stream operator, for every class. Note that this outputs
++ without any newlines or formatting, as opposed to Print(), which
++ includes tabs and new lines.
++
++ The operator<< and operator>> are not completely symmetric. Writing
++ a node to a stream is very well defined. You'll get a nice stream
++ of output, without any extra whitespace or newlines.
++
++ But reading is not as well defined. (As it always is.) If you create
++ a TiXmlElement (for example) and read that from an input stream,
++ the text needs to define an element or junk will result. This is
++ true of all input streams, but it's worth keeping in mind.
++
++ A TiXmlDocument will read nodes until it reads a root element, and
++ all the children of that root element.
++ */
++ friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
++
++ /// Appends the XML node or attribute to a std::string.
++ friend std::string& operator<< (std::string& out, const TiXmlNode& base );
++
++ #else
++ // Used internally, not part of the public API.
++ friend TIXML_OSTREAM& operator<< (TIXML_OSTREAM& out, const TiXmlNode& base);
++ #endif
++
++ /** The types of XML nodes supported by TinyXml. (All the
++ unsupported types are picked up by UNKNOWN.)
++ */
++ enum NodeType
++ {
++ DOCUMENT,
++ ELEMENT,
++ COMMENT,
++ UNKNOWN,
++ TEXT,
++ DECLARATION,
++ TYPECOUNT
++ };
++
++ virtual ~TiXmlNode();
++
++ /** The meaning of 'value' changes for the specific type of
++ TiXmlNode.
++ @verbatim
++ Document: filename of the xml file
++ Element: name of the element
++ Comment: the comment text
++ Unknown: the tag contents
++ Text: the text string
++ @endverbatim
++
++ The subclasses will wrap this function.
++ */
++ const char * Value() const { return value.c_str (); }
++
++ /** Changes the value of the node. Defined as:
++ @verbatim
++ Document: filename of the xml file
++ Element: name of the element
++ Comment: the comment text
++ Unknown: the tag contents
++ Text: the text string
++ @endverbatim
++ */
++ void SetValue(const char * _value) { value = _value;}
++
++ #ifdef TIXML_USE_STL
++ /// STL std::string form.
++ void SetValue( const std::string& _value )
++ {
++ StringToBuffer buf( _value );
++ SetValue( buf.buffer ? buf.buffer : "" );
++ }
++ #endif
++
++ /// Delete all the children of this node. Does not affect 'this'.
++ void Clear();
++
++ /// One step up the DOM.
++ TiXmlNode* Parent() const { return parent; }
++
++ TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children.
++ TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found.
++
++ TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children.
++ TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children.
++
++ #ifdef TIXML_USE_STL
++ TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form.
++ TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /** An alternate way to walk the children of a node.
++ One way to iterate over nodes is:
++ @verbatim
++ for( child = parent->FirstChild(); child; child = child->NextSibling() )
++ @endverbatim
++
++ IterateChildren does the same thing with the syntax:
++ @verbatim
++ child = 0;
++ while( child = parent->IterateChildren( child ) )
++ @endverbatim
++
++ IterateChildren takes the previous child as input and finds
++ the next one. If the previous child is null, it returns the
++ first. IterateChildren will return null when done.
++ */
++ TiXmlNode* IterateChildren( TiXmlNode* previous ) const;
++
++ /// This flavor of IterateChildren searches for children with a particular 'value'
++ TiXmlNode* IterateChildren( const char * value, TiXmlNode* previous ) const;
++
++ #ifdef TIXML_USE_STL
++ TiXmlNode* IterateChildren( const std::string& _value, TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
++ #endif
++
++ /** Add a new node related to this. Adds a child past the LastChild.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* InsertEndChild( const TiXmlNode& addThis );
++
++
++ /** Add a new node related to this. Adds a child past the LastChild.
++
++ NOTE: the node to be added is passed by pointer, and will be
++ henceforth owned (and deleted) by tinyXml. This method is efficient
++ and avoids an extra copy, but should be used with care as it
++ uses a different memory model than the other insert functions.
++
++ @sa InsertEndChild
++ */
++ TiXmlNode* LinkEndChild( TiXmlNode* addThis );
++
++ /** Add a new node related to this. Adds a child before the specified child.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );
++
++ /** Add a new node related to this. Adds a child after the specified child.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis );
++
++ /** Replace a child of this node.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );
++
++ /// Delete a child of this node.
++ bool RemoveChild( TiXmlNode* removeThis );
++
++ /// Navigate to a sibling node.
++ TiXmlNode* PreviousSibling() const { return prev; }
++
++ /// Navigate to a sibling node.
++ TiXmlNode* PreviousSibling( const char * ) const;
++
++ #ifdef TIXML_USE_STL
++ TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
++ TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /// Navigate to a sibling node.
++ TiXmlNode* NextSibling() const { return next; }
++
++ /// Navigate to a sibling node with the given 'value'.
++ TiXmlNode* NextSibling( const char * ) const;
++
++ /** Convenience function to get through elements.
++ Calls NextSibling and ToElement. Will skip all non-Element
++ nodes. Returns 0 if there is not another element.
++ */
++ TiXmlElement* NextSiblingElement() const;
++
++ /** Convenience function to get through elements.
++ Calls NextSibling and ToElement. Will skip all non-Element
++ nodes. Returns 0 if there is not another element.
++ */
++ TiXmlElement* NextSiblingElement( const char * ) const;
++
++ #ifdef TIXML_USE_STL
++ TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /// Convenience function to get through elements.
++ TiXmlElement* FirstChildElement() const;
++
++ /// Convenience function to get through elements.
++ TiXmlElement* FirstChildElement( const char * value ) const;
++
++ #ifdef TIXML_USE_STL
++ TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /** Query the type (as an enumerated value, above) of this node.
++ The possible types are: DOCUMENT, ELEMENT, COMMENT,
++ UNKNOWN, TEXT, and DECLARATION.
++ */
++ virtual int Type() const { return type; }
++
++ /** Return a pointer to the Document this node lives in.
++ Returns null if not in a document.
++ */
++ TiXmlDocument* GetDocument() const;
++
++ /// Returns true if this node has no children.
++ bool NoChildren() const { return !firstChild; }
++
++ TiXmlDocument* ToDocument() const { return ( this && type == DOCUMENT ) ? (TiXmlDocument*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++ TiXmlElement* ToElement() const { return ( this && type == ELEMENT ) ? (TiXmlElement*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++ TiXmlComment* ToComment() const { return ( this && type == COMMENT ) ? (TiXmlComment*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++ TiXmlUnknown* ToUnknown() const { return ( this && type == UNKNOWN ) ? (TiXmlUnknown*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++ TiXmlText* ToText() const { return ( this && type == TEXT ) ? (TiXmlText*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++ TiXmlDeclaration* ToDeclaration() const { return ( this && type == DECLARATION ) ? (TiXmlDeclaration*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++
++ /** Create an exact duplicate of this node and return it. The memory must be deleted
++ by the caller.
++ */
++ virtual TiXmlNode* Clone() const = 0;
++
++protected:
++ TiXmlNode( NodeType _type );
++
++ // Copy to the allocated object. Shared functionality between Clone, Copy constructor,
++ // and the assignment operator.
++ void CopyTo( TiXmlNode* target ) const;
++
++ #ifdef TIXML_USE_STL
++ // The real work of the input operator.
++ virtual void StreamIn( TIXML_ISTREAM* in, TIXML_STRING* tag ) = 0;
++ #endif
++
++ // Figure out what is at *p, and parse it. Returns null if it is not an xml node.
++ TiXmlNode* Identify( const char* start, TiXmlEncoding encoding );
++
++ // Internal Value function returning a TIXML_STRING
++ const TIXML_STRING& SValue() const { return value ; }
++
++ TiXmlNode* parent;
++ NodeType type;
++
++ TiXmlNode* firstChild;
++ TiXmlNode* lastChild;
++
++ TIXML_STRING value;
++
++ TiXmlNode* prev;
++ TiXmlNode* next;
++
++private:
++ TiXmlNode( const TiXmlNode& ); // not implemented.
++ void operator=( const TiXmlNode& base ); // not allowed.
++};
++
++
++/** An attribute is a name-value pair. Elements have an arbitrary
++ number of attributes, each with a unique name.
++
++ @note The attributes are not TiXmlNodes, since they are not
++ part of the tinyXML document object model. There are other
++ suggested ways to look at this problem.
++*/
++class TiXmlAttribute : public TiXmlBase
++{
++ friend class TiXmlAttributeSet;
++
++public:
++ /// Construct an empty attribute.
++ TiXmlAttribute() : TiXmlBase()
++ {
++ document = 0;
++ prev = next = 0;
++ }
++
++ #ifdef TIXML_USE_STL
++ /// std::string constructor.
++ TiXmlAttribute( const std::string& _name, const std::string& _value )
++ {
++ name = _name;
++ value = _value;
++ document = 0;
++ prev = next = 0;
++ }
++ #endif
++
++ /// Construct an attribute with a name and value.
++ TiXmlAttribute( const char * _name, const char * _value )
++ {
++ name = _name;
++ value = _value;
++ document = 0;
++ prev = next = 0;
++ }
++
++ const char* Name() const { return name.c_str (); } ///< Return the name of this attribute.
++ const char* Value() const { return value.c_str (); } ///< Return the value of this attribute.
++ const int IntValue() const; ///< Return the value of this attribute, converted to an integer.
++ const double DoubleValue() const; ///< Return the value of this attribute, converted to a double.
++
++ /** QueryIntValue examines the value string. It is an alternative to the
++ IntValue() method with richer error checking.
++ If the value is an integer, it is stored in 'value' and
++ the call returns TIXML_SUCCESS. If it is not
++ an integer, it returns TIXML_WRONG_TYPE.
++
++ A specialized but useful call. Note that for success it returns 0,
++ which is the opposite of almost all other TinyXml calls.
++ */
++ int QueryIntValue( int* value ) const;
++ /// QueryDoubleValue examines the value string. See QueryIntValue().
++ int QueryDoubleValue( double* value ) const;
++
++ void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute.
++ void SetValue( const char* _value ) { value = _value; } ///< Set the value.
++
++ void SetIntValue( int value ); ///< Set the value from an integer.
++ void SetDoubleValue( double value ); ///< Set the value from a double.
++
++ #ifdef TIXML_USE_STL
++ /// STL std::string form.
++ void SetName( const std::string& _name )
++ {
++ StringToBuffer buf( _name );
++ SetName ( buf.buffer ? buf.buffer : "error" );
++ }
++ /// STL std::string form.
++ void SetValue( const std::string& _value )
++ {
++ StringToBuffer buf( _value );
++ SetValue( buf.buffer ? buf.buffer : "error" );
++ }
++ #endif
++
++ /// Get the next sibling attribute in the DOM. Returns null at end.
++ TiXmlAttribute* Next() const;
++ /// Get the previous sibling attribute in the DOM. Returns null at beginning.
++ TiXmlAttribute* Previous() const;
++
++ bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; }
++ bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; }
++ bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; }
++
++ /* Attribute parsing starts: first letter of the name
++ returns: the next char after the value end quote
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++ // Prints this Attribute to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ virtual void StreamOut( TIXML_OSTREAM * out ) const;
++ // [internal use]
++ // Set the document pointer so the attribute can report errors.
++ void SetDocument( TiXmlDocument* doc ) { document = doc; }
++
++private:
++ TiXmlAttribute( const TiXmlAttribute& ); // not implemented.
++ void operator=( const TiXmlAttribute& base ); // not allowed.
++
++ TiXmlDocument* document; // A pointer back to a document, for error reporting.
++ TIXML_STRING name;
++ TIXML_STRING value;
++ TiXmlAttribute* prev;
++ TiXmlAttribute* next;
++};
++
++
++/* A class used to manage a group of attributes.
++ It is only used internally, both by the ELEMENT and the DECLARATION.
++
++ The set can be changed transparent to the Element and Declaration
++ classes that use it, but NOT transparent to the Attribute
++ which has to implement a next() and previous() method. Which makes
++ it a bit problematic and prevents the use of STL.
++
++ This version is implemented with circular lists because:
++ - I like circular lists
++ - it demonstrates some independence from the (typical) doubly linked list.
++*/
++class TiXmlAttributeSet
++{
++public:
++ TiXmlAttributeSet();
++ ~TiXmlAttributeSet();
++
++ void Add( TiXmlAttribute* attribute );
++ void Remove( TiXmlAttribute* attribute );
++
++ TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
++ TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
++ TiXmlAttribute* Find( const char * name ) const;
++
++private:
++ TiXmlAttribute sentinel;
++};
++
++
++/** The element is a container class. It has a value, the element name,
++ and can contain other elements, text, comments, and unknowns.
++ Elements also contain an arbitrary number of attributes.
++*/
++class TiXmlElement : public TiXmlNode
++{
++public:
++ /// Construct an element.
++ TiXmlElement (const char * in_value);
++
++ #ifdef TIXML_USE_STL
++ /// std::string constructor.
++ TiXmlElement( const std::string& _value );
++ #endif
++
++ TiXmlElement( const TiXmlElement& );
++
++ void operator=( const TiXmlElement& base );
++
++ virtual ~TiXmlElement();
++
++ /** Given an attribute name, Attribute() returns the value
++ for the attribute of that name, or null if none exists.
++ */
++ const char* Attribute( const char* name ) const;
++
++ /** Given an attribute name, Attribute() returns the value
++ for the attribute of that name, or null if none exists.
++ If the attribute exists and can be converted to an integer,
++ the integer value will be put in the return 'i', if 'i'
++ is non-null.
++ */
++ const char* Attribute( const char* name, int* i ) const;
++
++ /** Given an attribute name, Attribute() returns the value
++ for the attribute of that name, or null if none exists.
++ If the attribute exists and can be converted to an double,
++ the double value will be put in the return 'd', if 'd'
++ is non-null.
++ */
++ const char* Attribute( const char* name, double* d ) const;
++
++ /** QueryIntAttribute examines the attribute - it is an alternative to the
++ Attribute() method with richer error checking.
++ If the attribute is an integer, it is stored in 'value' and
++ the call returns TIXML_SUCCESS. If it is not
++ an integer, it returns TIXML_WRONG_TYPE. If the attribute
++ does not exist, then TIXML_NO_ATTRIBUTE is returned.
++ */
++ int QueryIntAttribute( const char* name, int* value ) const;
++ /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute().
++ int QueryDoubleAttribute( const char* name, double* value ) const;
++
++ /** Sets an attribute of name to a given value. The attribute
++ will be created if it does not exist, or changed if it does.
++ */
++ void SetAttribute( const char* name, const char * value );
++
++ #ifdef TIXML_USE_STL
++ const char* Attribute( const std::string& name ) const { return Attribute( name.c_str() ); }
++ const char* Attribute( const std::string& name, int* i ) const { return Attribute( name.c_str(), i ); }
++ const char* Attribute( const std::string& name, double* d ) const { return Attribute( name.c_str(), d ); }
++ int QueryIntAttribute( const std::string& name, int* value ) const { return QueryIntAttribute( name.c_str(), value ); }
++ int QueryDoubleAttribute( const std::string& name, double* value ) const { return QueryDoubleAttribute( name.c_str(), value ); }
++
++ /// STL std::string form.
++ void SetAttribute( const std::string& name, const std::string& _value )
++ {
++ StringToBuffer n( name );
++ StringToBuffer v( _value );
++ if ( n.buffer && v.buffer )
++ SetAttribute (n.buffer, v.buffer );
++ }
++ ///< STL std::string form.
++ void SetAttribute( const std::string& name, int _value )
++ {
++ StringToBuffer n( name );
++ if ( n.buffer )
++ SetAttribute (n.buffer, _value);
++ }
++ #endif
++
++ /** Sets an attribute of name to a given value. The attribute
++ will be created if it does not exist, or changed if it does.
++ */
++ void SetAttribute( const char * name, int value );
++
++ /** Sets an attribute of name to a given value. The attribute
++ will be created if it does not exist, or changed if it does.
++ */
++ void SetDoubleAttribute( const char * name, double value );
++
++ /** Deletes an attribute with the given name.
++ */
++ void RemoveAttribute( const char * name );
++ #ifdef TIXML_USE_STL
++ void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form.
++ #endif
++
++ TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element.
++ TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element.
++
++ /// Creates a new Element and returns it - the returned element is a copy.
++ virtual TiXmlNode* Clone() const;
++ // Print the Element to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ /* Attribtue parsing starts: next char past '<'
++ returns: next char past '>'
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++
++ void CopyTo( TiXmlElement* target ) const;
++ void ClearThis(); // like clear, but initializes 'this' object as well
++
++ // Used to be public [internal use]
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++ virtual void StreamOut( TIXML_OSTREAM * out ) const;
++
++ /* [internal use]
++ Reads the "value" of the element -- another element, or text.
++ This should terminate with the current end tag.
++ */
++ const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );
++
++private:
++
++ TiXmlAttributeSet attributeSet;
++};
++
++
++/** An XML comment.
++*/
++class TiXmlComment : public TiXmlNode
++{
++public:
++ /// Constructs an empty comment.
++ TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {}
++ TiXmlComment( const TiXmlComment& );
++ void operator=( const TiXmlComment& base );
++
++ virtual ~TiXmlComment() {}
++
++ /// Returns a copy of this Comment.
++ virtual TiXmlNode* Clone() const;
++ /// Write this Comment to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ /* Attribtue parsing starts: at the ! of the !--
++ returns: next char past '>'
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++ void CopyTo( TiXmlComment* target ) const;
++
++ // used to be public
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++ virtual void StreamOut( TIXML_OSTREAM * out ) const;
++
++private:
++
++};
++
++
++/** XML text. Contained in an element.
++*/
++class TiXmlText : public TiXmlNode
++{
++ friend class TiXmlElement;
++public:
++ /// Constructor.
++ TiXmlText (const char * initValue) : TiXmlNode (TiXmlNode::TEXT)
++ {
++ SetValue( initValue );
++ }
++ virtual ~TiXmlText() {}
++
++ #ifdef TIXML_USE_STL
++ /// Constructor.
++ TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT)
++ {
++ SetValue( initValue );
++ }
++ #endif
++
++ TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); }
++ void operator=( const TiXmlText& base ) { base.CopyTo( this ); }
++
++ /// Write this text object to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected :
++ /// [internal use] Creates a new Element and returns it.
++ virtual TiXmlNode* Clone() const;
++ void CopyTo( TiXmlText* target ) const;
++
++ virtual void StreamOut ( TIXML_OSTREAM * out ) const;
++ bool Blank() const; // returns true if all white space and new lines
++ // [internal use]
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++
++private:
++};
++
++
++/** In correct XML the declaration is the first entry in the file.
++ @verbatim
++ <?xml version="1.0" standalone="yes"?>
++ @endverbatim
++
++ TinyXml will happily read or write files without a declaration,
++ however. There are 3 possible attributes to the declaration:
++ version, encoding, and standalone.
++
++ Note: In this version of the code, the attributes are
++ handled as special cases, not generic attributes, simply
++ because there can only be at most 3 and they are always the same.
++*/
++class TiXmlDeclaration : public TiXmlNode
++{
++public:
++ /// Construct an empty declaration.
++ TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {}
++
++#ifdef TIXML_USE_STL
++ /// Constructor.
++ TiXmlDeclaration( const std::string& _version,
++ const std::string& _encoding,
++ const std::string& _standalone );
++#endif
++
++ /// Construct.
++ TiXmlDeclaration( const char* _version,
++ const char* _encoding,
++ const char* _standalone );
++
++ TiXmlDeclaration( const TiXmlDeclaration& copy );
++ void operator=( const TiXmlDeclaration& copy );
++
++ virtual ~TiXmlDeclaration() {}
++
++ /// Version. Will return an empty string if none was found.
++ const char *Version() const { return version.c_str (); }
++ /// Encoding. Will return an empty string if none was found.
++ const char *Encoding() const { return encoding.c_str (); }
++ /// Is this a standalone document?
++ const char *Standalone() const { return standalone.c_str (); }
++
++ /// Creates a copy of this Declaration and returns it.
++ virtual TiXmlNode* Clone() const;
++ /// Print this declaration to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++ void CopyTo( TiXmlDeclaration* target ) const;
++ // used to be public
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++ virtual void StreamOut ( TIXML_OSTREAM * out) const;
++
++private:
++
++ TIXML_STRING version;
++ TIXML_STRING encoding;
++ TIXML_STRING standalone;
++};
++
++
++/** Any tag that tinyXml doesn't recognize is saved as an
++ unknown. It is a tag of text, but should not be modified.
++ It will be written back to the XML, unchanged, when the file
++ is saved.
++
++ DTD tags get thrown into TiXmlUnknowns.
++*/
++class TiXmlUnknown : public TiXmlNode
++{
++public:
++ TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {}
++ virtual ~TiXmlUnknown() {}
++
++ TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); }
++ void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); }
++
++ /// Creates a copy of this Unknown and returns it.
++ virtual TiXmlNode* Clone() const;
++ /// Print this Unknown to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++ void CopyTo( TiXmlUnknown* target ) const;
++
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++ virtual void StreamOut ( TIXML_OSTREAM * out ) const;
++
++private:
++
++};
++
++
++/** Always the top level node. A document binds together all the
++ XML pieces. It can be saved, loaded, and printed to the screen.
++ The 'value' of a document node is the xml file name.
++*/
++class TiXmlDocument : public TiXmlNode
++{
++public:
++ /// Create an empty document, that has no name.
++ TiXmlDocument();
++ /// Create a document with a name. The name of the document is also the filename of the xml.
++ TiXmlDocument( const char * documentName );
++
++ #ifdef TIXML_USE_STL
++ /// Constructor.
++ TiXmlDocument( const std::string& documentName );
++ #endif
++
++ TiXmlDocument( const TiXmlDocument& copy );
++ void operator=( const TiXmlDocument& copy );
++
++ virtual ~TiXmlDocument() {}
++
++ /** Load a file using the current document value.
++ Returns true if successful. Will delete any existing
++ document data before loading.
++ */
++ bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++ /// Save a file using the current document value. Returns true if successful.
++ bool SaveFile() const;
++ /// Load a file using the given filename. Returns true if successful.
++ bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++ /// Save a file using the given filename. Returns true if successful.
++ bool SaveFile( const char * filename ) const;
++
++ #ifdef TIXML_USE_STL
++ bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version.
++ {
++ StringToBuffer f( filename );
++ return ( f.buffer && LoadFile( f.buffer, encoding ));
++ }
++ bool SaveFile( const std::string& filename ) const ///< STL std::string version.
++ {
++ StringToBuffer f( filename );
++ return ( f.buffer && SaveFile( f.buffer ));
++ }
++ #endif
++
++ /** Parse the given null terminated block of xml data. Passing in an encoding to this
++ method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml
++ to use that encoding, regardless of what TinyXml might otherwise try to detect.
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++
++ /** Get the root element -- the only top level element -- of the document.
++ In well formed XML, there should only be one. TinyXml is tolerant of
++ multiple elements at the document level.
++ */
++ TiXmlElement* RootElement() const { return FirstChildElement(); }
++
++ /** If an error occurs, Error will be set to true. Also,
++ - The ErrorId() will contain the integer identifier of the error (not generally useful)
++ - The ErrorDesc() method will return the name of the error. (very useful)
++ - The ErrorRow() and ErrorCol() will return the location of the error (if known)
++ */
++ bool Error() const { return error; }
++
++ /// Contains a textual (english) description of the error if one occurs.
++ const char * ErrorDesc() const { return errorDesc.c_str (); }
++
++ /** Generally, you probably want the error string ( ErrorDesc() ). But if you
++ prefer the ErrorId, this function will fetch it.
++ */
++ const int ErrorId() const { return errorId; }
++
++ /** Returns the location (if known) of the error. The first column is column 1,
++ and the first row is row 1. A value of 0 means the row and column wasn't applicable
++ (memory errors, for example, have no row/column) or the parser lost the error. (An
++ error in the error reporting, in that case.)
++
++ @sa SetTabSize, Row, Column
++ */
++ int ErrorRow() { return errorLocation.row+1; }
++ int ErrorCol() { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow()
++
++ /** By calling this method, with a tab size
++ greater than 0, the row and column of each node and attribute is stored
++ when the file is loaded. Very useful for tracking the DOM back in to
++ the source file.
++
++ The tab size is required for calculating the location of nodes. If not
++ set, the default of 4 is used. The tabsize is set per document. Setting
++ the tabsize to 0 disables row/column tracking.
++
++ Note that row and column tracking is not supported when using operator>>.
++
++ The tab size needs to be enabled before the parse or load. Correct usage:
++ @verbatim
++ TiXmlDocument doc;
++ doc.SetTabSize( 8 );
++ doc.Load( "myfile.xml" );
++ @endverbatim
++
++ @sa Row, Column
++ */
++ void SetTabSize( int _tabsize ) { tabsize = _tabsize; }
++
++ int TabSize() const { return tabsize; }
++
++ /** If you have handled the error, it can be reset with this call. The error
++ state is automatically cleared if you Parse a new XML block.
++ */
++ void ClearError() { error = false;
++ errorId = 0;
++ errorDesc = "";
++ errorLocation.row = errorLocation.col = 0;
++ //errorLocation.last = 0;
++ }
++
++ /** Dump the document to standard out. */
++ void Print() const { Print( stdout, 0 ); }
++
++ /// Print this Document to a FILE stream.
++ virtual void Print( FILE* cfile, int depth = 0 ) const;
++ // [internal use]
++ void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding );
++
++protected :
++ virtual void StreamOut ( TIXML_OSTREAM * out) const;
++ // [internal use]
++ virtual TiXmlNode* Clone() const;
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++
++private:
++ void CopyTo( TiXmlDocument* target ) const;
++
++ bool error;
++ int errorId;
++ TIXML_STRING errorDesc;
++ int tabsize;
++ TiXmlCursor errorLocation;
++};
++
++
++/**
++ A TiXmlHandle is a class that wraps a node pointer with null checks; this is
++ an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml
++ DOM structure. It is a separate utility class.
++
++ Take an example:
++ @verbatim
++ <Document>
++ <Element attributeA = "valueA">
++ <Child attributeB = "value1" />
++ <Child attributeB = "value2" />
++ </Element>
++ <Document>
++ @endverbatim
++
++ Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
++ easy to write a *lot* of code that looks like:
++
++ @verbatim
++ TiXmlElement* root = document.FirstChildElement( "Document" );
++ if ( root )
++ {
++ TiXmlElement* element = root->FirstChildElement( "Element" );
++ if ( element )
++ {
++ TiXmlElement* child = element->FirstChildElement( "Child" );
++ if ( child )
++ {
++ TiXmlElement* child2 = child->NextSiblingElement( "Child" );
++ if ( child2 )
++ {
++ // Finally do something useful.
++ @endverbatim
++
++ And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity
++ of such code. A TiXmlHandle checks for null pointers so it is perfectly safe
++ and correct to use:
++
++ @verbatim
++ TiXmlHandle docHandle( &document );
++ TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).Element();
++ if ( child2 )
++ {
++ // do something useful
++ @endverbatim
++
++ Which is MUCH more concise and useful.
++
++ It is also safe to copy handles - internally they are nothing more than node pointers.
++ @verbatim
++ TiXmlHandle handleCopy = handle;
++ @endverbatim
++
++ What they should not be used for is iteration:
++
++ @verbatim
++ int i=0;
++ while ( true )
++ {
++ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).Element();
++ if ( !child )
++ break;
++ // do something
++ ++i;
++ }
++ @endverbatim
++
++ It seems reasonable, but it is in fact two embedded while loops. The Child method is
++ a linear walk to find the element, so this code would iterate much more than it needs
++ to. Instead, prefer:
++
++ @verbatim
++ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).Element();
++
++ for( child; child; child=child->NextSiblingElement() )
++ {
++ // do something
++ }
++ @endverbatim
++*/
++class TiXmlHandle
++{
++public:
++ /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
++ TiXmlHandle( TiXmlNode* node ) { this->node = node; }
++ /// Copy constructor
++ TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; }
++ TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; }
++
++ /// Return a handle to the first child node.
++ TiXmlHandle FirstChild() const;
++ /// Return a handle to the first child node with the given name.
++ TiXmlHandle FirstChild( const char * value ) const;
++ /// Return a handle to the first child element.
++ TiXmlHandle FirstChildElement() const;
++ /// Return a handle to the first child element with the given name.
++ TiXmlHandle FirstChildElement( const char * value ) const;
++
++ /** Return a handle to the "index" child with the given name.
++ The first child is 0, the second 1, etc.
++ */
++ TiXmlHandle Child( const char* value, int index ) const;
++ /** Return a handle to the "index" child.
++ The first child is 0, the second 1, etc.
++ */
++ TiXmlHandle Child( int index ) const;
++ /** Return a handle to the "index" child element with the given name.
++ The first child element is 0, the second 1, etc. Note that only TiXmlElements
++ are indexed: other types are not counted.
++ */
++ TiXmlHandle ChildElement( const char* value, int index ) const;
++ /** Return a handle to the "index" child element.
++ The first child element is 0, the second 1, etc. Note that only TiXmlElements
++ are indexed: other types are not counted.
++ */
++ TiXmlHandle ChildElement( int index ) const;
++
++ #ifdef TIXML_USE_STL
++ TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); }
++ TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); }
++
++ TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); }
++ TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); }
++ #endif
++
++ /// Return the handle as a TiXmlNode. This may return null.
++ TiXmlNode* Node() const { return node; }
++ /// Return the handle as a TiXmlElement. This may return null.
++ TiXmlElement* Element() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }
++ /// Return the handle as a TiXmlText. This may return null.
++ TiXmlText* Text() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }
++ /// Return the handle as a TiXmlUnknown. This may return null;
++ TiXmlUnknown* Unknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }
++
++private:
++ TiXmlNode* node;
++};
++
++
++#endif
++#endif /* SETUP */
+diff -ruNp vdr-1.6.0-2/tinyxmlparser.c vdr-1.6.0-2-extensions/tinyxmlparser.c
+--- vdr-1.6.0-2/tinyxmlparser.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/tinyxmlparser.c 2009-04-09 20:48:27.000000000 +0200
+@@ -0,0 +1,1494 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include "tinyxml.h"
++#include <ctype.h>
++
++//#define DEBUG_PARSER
++
++// Note tha "PutString" hardcodes the same list. This
++// is less flexible than it appears. Changing the entries
++// or order will break putstring.
++TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] =
++{
++ { "&amp;", 5, '&' },
++ { "&lt;", 4, '<' },
++ { "&gt;", 4, '>' },
++ { "&quot;", 6, '\"' },
++ { "&apos;", 6, '\'' }
++};
++
++// Bunch of unicode info at:
++// http://www.unicode.org/faq/utf_bom.html
++// Including the basic of this table, which determines the #bytes in the
++// sequence from the lead byte. 1 placed for invalid sequences --
++// although the result will be junk, pass it through as much as possible.
++// Beware of the non-characters in UTF-8:
++// ef bb bf (Microsoft "lead bytes")
++// ef bf be
++// ef bf bf
++
++
++
++const int TiXmlBase::utf8ByteTable[256] =
++{
++ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0
++ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte
++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
++ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte
++ 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
++};
++
++
++void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
++{
++ const unsigned long BYTE_MASK = 0xBF;
++ const unsigned long BYTE_MARK = 0x80;
++ const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
++
++ if (input < 0x80)
++ *length = 1;
++ else if ( input < 0x800 )
++ *length = 2;
++ else if ( input < 0x10000 )
++ *length = 3;
++ else if ( input < 0x200000 )
++ *length = 4;
++ else
++ { *length = 0; return; } // This code won't covert this correctly anyway.
++
++ output += *length;
++
++ // Scary scary fall throughs.
++ switch (*length)
++ {
++ case 4:
++ --output;
++ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
++ input >>= 6;
++ case 3:
++ --output;
++ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
++ input >>= 6;
++ case 2:
++ --output;
++ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
++ input >>= 6;
++ case 1:
++ --output;
++ *output = (char)(input | FIRST_BYTE_MARK[*length]);
++ }
++}
++
++
++/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding encoding )
++{
++ // This will only work for low-ascii, everything else is assumed to be a valid
++ // letter. I'm not sure this is the best approach, but it is quite tricky trying
++ // to figure out alhabetical vs. not across encoding. So take a very
++ // conservative approach.
++
++// if ( encoding == TIXML_ENCODING_UTF8 )
++// {
++ if ( anyByte < 127 )
++ return isalpha( anyByte );
++ else
++ return 1; // What else to do? The unicode set is huge...get the english ones right.
++// }
++// else
++// {
++// return isalpha( anyByte );
++// }
++}
++
++
++/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding )
++{
++ // This will only work for low-ascii, everything else is assumed to be a valid
++ // letter. I'm not sure this is the best approach, but it is quite tricky trying
++ // to figure out alhabetical vs. not across encoding. So take a very
++ // conservative approach.
++
++// if ( encoding == TIXML_ENCODING_UTF8 )
++// {
++ if ( anyByte < 127 )
++ return isalnum( anyByte );
++ else
++ return 1; // What else to do? The unicode set is huge...get the english ones right.
++// }
++// else
++// {
++// return isalnum( anyByte );
++// }
++}
++
++
++class TiXmlParsingData
++{
++ friend class TiXmlDocument;
++ public:
++ void Stamp( const char* now, TiXmlEncoding encoding );
++
++ const TiXmlCursor& Cursor() { return cursor; }
++
++ private:
++ // Only used by the document!
++ TiXmlParsingData( const char* start, int _tabsize, int row, int col )
++ {
++ assert( start );
++ stamp = start;
++ tabsize = _tabsize;
++ cursor.row = row;
++ cursor.col = col;
++ }
++
++ TiXmlCursor cursor;
++ const char* stamp;
++ int tabsize;
++};
++
++
++void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding )
++{
++ assert( now );
++
++ // Do nothing if the tabsize is 0.
++ if ( tabsize < 1 )
++ {
++ return;
++ }
++
++ // Get the current row, column.
++ int row = cursor.row;
++ int col = cursor.col;
++ const char* p = stamp;
++ assert( p );
++
++ while ( p < now )
++ {
++ // Code contributed by Fletcher Dunn: (modified by lee)
++ switch (*p) {
++ case 0:
++ // We *should* never get here, but in case we do, don't
++ // advance past the terminating null character, ever
++ return;
++
++ case '\r':
++ // bump down to the next line
++ ++row;
++ col = 0;
++ // Eat the character
++ ++p;
++
++ // Check for \r\n sequence, and treat this as a single character
++ if (*p == '\n') {
++ ++p;
++ }
++ break;
++
++ case '\n':
++ // bump down to the next line
++ ++row;
++ col = 0;
++
++ // Eat the character
++ ++p;
++
++ // Check for \n\r sequence, and treat this as a single
++ // character. (Yes, this bizarre thing does occur still
++ // on some arcane platforms...)
++ if (*p == '\r') {
++ ++p;
++ }
++ break;
++
++ case '\t':
++ // Eat the character
++ ++p;
++
++ // Skip to next tab stop
++ col = (col / tabsize + 1) * tabsize;
++ break;
++
++ case (char)(0xef):
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ if ( *(p+1) && *(p+2) )
++ {
++ // In these cases, don't advance the column. These are
++ // 0-width spaces.
++ if ( *(p+1)==(char)(0xbb) && *(p+2)==(char)(0xbf) )
++ p += 3;
++ else if ( *(p+1)==(char)(0xbf) && *(p+2)==(char)(0xbe) )
++ p += 3;
++ else if ( *(p+1)==(char)(0xbf) && *(p+2)==(char)(0xbf) )
++ p += 3;
++ else
++ { p +=3; ++col; } // A normal character.
++ }
++ }
++ else
++ {
++ ++p;
++ ++col;
++ }
++ break;
++
++ default:
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ // Eat the 1 to 4 byte utf8 character.
++ int step = TiXmlBase::utf8ByteTable[*((unsigned char*)p)];
++ if ( step == 0 )
++ step = 1; // Error case from bad encoding, but handle gracefully.
++ p += step;
++
++ // Just advance one column, of course.
++ ++col;
++ }
++ else
++ {
++ ++p;
++ ++col;
++ }
++ break;
++ }
++ }
++ cursor.row = row;
++ cursor.col = col;
++ assert( cursor.row >= -1 );
++ assert( cursor.col >= -1 );
++ stamp = p;
++ assert( stamp );
++}
++
++
++const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )
++{
++ if ( !p || !*p )
++ {
++ return 0;
++ }
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ while ( *p )
++ {
++ // Skip the stupid Microsoft UTF-8 Byte order marks
++ if ( *(p+0)==(char) 0xef
++ && *(p+1)==(char) 0xbb
++ && *(p+2)==(char) 0xbf )
++ {
++ p += 3;
++ continue;
++ }
++ else if(*(p+0)==(char) 0xef
++ && *(p+1)==(char) 0xbf
++ && *(p+2)==(char) 0xbe )
++ {
++ p += 3;
++ continue;
++ }
++ else if(*(p+0)==(char) 0xef
++ && *(p+1)==(char) 0xbf
++ && *(p+2)==(char) 0xbf )
++ {
++ p += 3;
++ continue;
++ }
++
++ if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space.
++ ++p;
++ else
++ break;
++ }
++ }
++ else
++ {
++ while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' )
++ ++p;
++ }
++
++ return p;
++}
++
++#ifdef TIXML_USE_STL
++/*static*/ bool TiXmlBase::StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ for( ;; )
++ {
++ if ( !in->good() ) return false;
++
++ int c = in->peek();
++ // At this scope, we can't get to a document. So fail silently.
++ if ( !IsWhiteSpace( c ) || c <= 0 )
++ return true;
++
++ *tag += (char) in->get();
++ }
++}
++
++/*static*/ bool TiXmlBase::StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag )
++{
++ //assert( character > 0 && character < 128 ); // else it won't work in utf-8
++ while ( in->good() )
++ {
++ int c = in->peek();
++ if ( c == character )
++ return true;
++ if ( c <= 0 ) // Silent failure: can't get document at this scope
++ return false;
++
++ in->get();
++ *tag += (char) c;
++ }
++ return false;
++}
++#endif
++
++const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding )
++{
++ *name = "";
++ assert( p );
++
++ // Names start with letters or underscores.
++ // Of course, in unicode, tinyxml has no idea what a letter *is*. The
++ // algorithm is generous.
++ //
++ // After that, they can be letters, underscores, numbers,
++ // hyphens, or colons. (Colons are valid ony for namespaces,
++ // but tinyxml can't tell namespaces from names.)
++ if ( p && *p
++ && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) )
++ {
++ while( p && *p
++ && ( IsAlphaNum( (unsigned char ) *p, encoding )
++ || *p == '_'
++ || *p == '-'
++ || *p == '.'
++ || *p == ':' ) )
++ {
++ (*name) += *p;
++ ++p;
++ }
++ return p;
++ }
++ return 0;
++}
++
++const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding )
++{
++ // Presume an entity, and pull it out.
++ TIXML_STRING ent;
++ int i;
++ *length = 0;
++
++ if ( *(p+1) && *(p+1) == '#' && *(p+2) )
++ {
++ unsigned long ucs = 0;
++ unsigned delta = 0;
++ unsigned mult = 1;
++
++ if ( *(p+2) == 'x' )
++ {
++ // Hexadecimal.
++ if ( !*(p+3) ) return 0;
++
++ const char* q = p+3;
++ q = strchr( q, ';' );
++
++ if ( !q || !*q ) return 0;
++
++ delta = q-p;
++ --q;
++
++ while ( *q != 'x' )
++ {
++ if ( *q >= '0' && *q <= '9' )
++ ucs += mult * (*q - '0');
++ else if ( *q >= 'a' && *q <= 'f' )
++ ucs += mult * (*q - 'a' + 10);
++ else if ( *q >= 'A' && *q <= 'F' )
++ ucs += mult * (*q - 'A' + 10 );
++ else
++ return 0;
++ mult *= 16;
++ --q;
++ }
++ }
++ else
++ {
++ // Decimal.
++ if ( !*(p+2) ) return 0;
++
++ const char* q = p+2;
++ q = strchr( q, ';' );
++
++ if ( !q || !*q ) return 0;
++
++ delta = q-p;
++ --q;
++
++ while ( *q != '#' )
++ {
++ if ( *q >= '0' && *q <= '9' )
++ ucs += mult * (*q - '0');
++ else
++ return 0;
++ mult *= 10;
++ --q;
++ }
++ }
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ // convert the UCS to UTF-8
++ ConvertUTF32ToUTF8( ucs, value, length );
++ }
++ else
++ {
++ *value = (char)ucs;
++ *length = 1;
++ }
++ return p + delta + 1;
++ }
++
++ // Now try to match it.
++ for( i=0; i<NUM_ENTITY; ++i )
++ {
++ if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 )
++ {
++ assert( strlen( entity[i].str ) == entity[i].strLength );
++ *value = entity[i].chr;
++ *length = 1;
++ return ( p + entity[i].strLength );
++ }
++ }
++
++ // So it wasn't an entity, its unrecognized, or something like that.
++ *value = *p; // Don't put back the last one, since we return it!
++ return p+1;
++}
++
++
++bool TiXmlBase::StringEqual( const char* p,
++ const char* tag,
++ bool ignoreCase,
++ TiXmlEncoding encoding )
++{
++ assert( p );
++ assert( tag );
++ if ( !p || !*p )
++ {
++ assert( 0 );
++ return false;
++ }
++
++ const char* q = p;
++
++ if ( ignoreCase )
++ {
++ while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) )
++ {
++ ++q;
++ ++tag;
++ }
++
++ if ( *tag == 0 )
++ return true;
++ }
++ else
++ {
++ while ( *q && *tag && *q == *tag )
++ {
++ ++q;
++ ++tag;
++ }
++
++ if ( *tag == 0 ) // Have we found the end of the tag, and everything equal?
++ return true;
++ }
++ return false;
++}
++
++const char* TiXmlBase::ReadText( const char* p,
++ TIXML_STRING * text,
++ bool trimWhiteSpace,
++ const char* endTag,
++ bool caseInsensitive,
++ TiXmlEncoding encoding )
++{
++ *text = "";
++ if ( !trimWhiteSpace // certain tags always keep whitespace
++ || !condenseWhiteSpace ) // if true, whitespace is always kept
++ {
++ // Keep all the white space.
++ while ( p && *p
++ && !StringEqual( p, endTag, caseInsensitive, encoding )
++ )
++ {
++ int len;
++ char cArr[4] = { 0, 0, 0, 0 };
++ p = GetChar( p, cArr, &len, encoding );
++ text->append( cArr, len );
++ }
++ }
++ else
++ {
++ bool whitespace = false;
++
++ // Remove leading white space:
++ p = SkipWhiteSpace( p, encoding );
++ while ( p && *p
++ && !StringEqual( p, endTag, caseInsensitive, encoding ) )
++ {
++ if ( *p == '\r' || *p == '\n' )
++ {
++ whitespace = true;
++ ++p;
++ }
++ else if ( IsWhiteSpace( *p ) )
++ {
++ whitespace = true;
++ ++p;
++ }
++ else
++ {
++ // If we've found whitespace, add it before the
++ // new character. Any whitespace just becomes a space.
++ if ( whitespace )
++ {
++ (*text) += ' ';
++ whitespace = false;
++ }
++ int len;
++ char cArr[4] = { 0, 0, 0, 0 };
++ p = GetChar( p, cArr, &len, encoding );
++ if ( len == 1 )
++ (*text) += cArr[0]; // more efficient
++ else
++ text->append( cArr, len );
++ }
++ }
++ }
++ return p + strlen( endTag );
++}
++
++#ifdef TIXML_USE_STL
++
++void TiXmlDocument::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ // The basic issue with a document is that we don't know what we're
++ // streaming. Read something presumed to be a tag (and hope), then
++ // identify it, and call the appropriate stream method on the tag.
++ //
++ // This "pre-streaming" will never read the closing ">" so the
++ // sub-tag can orient itself.
++
++ if ( !StreamTo( in, '<', tag ) )
++ {
++ SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ while ( in->good() )
++ {
++ int tagIndex = (int) tag->length();
++ while ( in->good() && in->peek() != '>' )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ break;
++ }
++ (*tag) += (char) c;
++ }
++
++ if ( in->good() )
++ {
++ // We now have something we presume to be a node of
++ // some sort. Identify it, and call the node to
++ // continue streaming.
++ TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING );
++
++ if ( node )
++ {
++ node->StreamIn( in, tag );
++ bool isElement = node->ToElement() != 0;
++ delete node;
++ node = 0;
++
++ // If this is the root element, we're done. Parsing will be
++ // done by the >> operator.
++ if ( isElement )
++ {
++ return;
++ }
++ }
++ else
++ {
++ SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ }
++ }
++ // We should have returned sooner.
++ SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
++}
++
++#endif
++
++const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding )
++{
++ ClearError();
++
++ // Parse away, at the document level. Since a document
++ // contains nothing but other tags, most of what happens
++ // here is skipping white space.
++ if ( !p || !*p )
++ {
++ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return 0;
++ }
++
++ // Note that, for a document, this needs to come
++ // before the while space skip, so that parsing
++ // starts from the pointer we are given.
++ location.Clear();
++ if ( prevData )
++ {
++ location.row = prevData->cursor.row;
++ location.col = prevData->cursor.col;
++ }
++ else
++ {
++ location.row = 0;
++ location.col = 0;
++ }
++ TiXmlParsingData data( p, TabSize(), location.row, location.col );
++ location = data.Cursor();
++
++ if ( encoding == TIXML_ENCODING_UNKNOWN )
++ {
++ // Check for the Microsoft UTF-8 lead bytes.
++ if ( *(p+0) && *(p+0) == (char)(0xef)
++ && *(p+1) && *(p+1) == (char)(0xbb)
++ && *(p+2) && *(p+2) == (char)(0xbf) )
++ {
++ encoding = TIXML_ENCODING_UTF8;
++ }
++ }
++
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p )
++ {
++ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return 0;
++ }
++
++ while ( p && *p )
++ {
++ TiXmlNode* node = Identify( p, encoding );
++ if ( node )
++ {
++ p = node->Parse( p, &data, encoding );
++ LinkEndChild( node );
++ }
++ else
++ {
++ break;
++ }
++
++ // Did we get encoding info?
++ if ( encoding == TIXML_ENCODING_UNKNOWN
++ && node->ToDeclaration() )
++ {
++ TiXmlDeclaration* dec = node->ToDeclaration();
++ const char* enc = dec->Encoding();
++ assert( enc );
++
++ if ( *enc == 0 )
++ encoding = TIXML_ENCODING_UTF8;
++ else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) )
++ encoding = TIXML_ENCODING_UTF8;
++ else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) )
++ encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice
++ else
++ encoding = TIXML_ENCODING_LEGACY;
++ }
++
++ p = SkipWhiteSpace( p, encoding );
++ }
++
++ // All is well.
++ return p;
++}
++
++void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ // The first error in a chain is more accurate - don't set again!
++ if ( error )
++ return;
++
++ assert( err > 0 && err < TIXML_ERROR_STRING_COUNT );
++ error = true;
++ errorId = err;
++ errorDesc = errorString[ errorId ];
++
++ errorLocation.Clear();
++ if ( pError && data )
++ {
++ //TiXmlParsingData data( pError, prevData );
++ data->Stamp( pError, encoding );
++ errorLocation = data->Cursor();
++ }
++}
++
++
++TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding )
++{
++ TiXmlNode* returnNode = 0;
++
++ p = SkipWhiteSpace( p, encoding );
++ if( !p || !*p || *p != '<' )
++ {
++ return 0;
++ }
++
++ TiXmlDocument* doc = GetDocument();
++ p = SkipWhiteSpace( p, encoding );
++
++ if ( !p || !*p )
++ {
++ return 0;
++ }
++
++ // What is this thing?
++ // - Elements start with a letter or underscore, but xml is reserved.
++ // - Comments: <!--
++ // - Decleration: <?xml
++ // - Everthing else is unknown to tinyxml.
++ //
++
++ const char* xmlHeader = { "<?xml" };
++ const char* commentHeader = { "<!--" };
++ const char* dtdHeader = { "<!" };
++
++ if ( StringEqual( p, xmlHeader, true, encoding ) )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Declaration\n" );
++ #endif
++ returnNode = new TiXmlDeclaration();
++ }
++ else if ( StringEqual( p, commentHeader, false, encoding ) )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Comment\n" );
++ #endif
++ returnNode = new TiXmlComment();
++ }
++ else if ( StringEqual( p, dtdHeader, false, encoding ) )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Unknown(1)\n" );
++ #endif
++ returnNode = new TiXmlUnknown();
++ }
++ else if ( IsAlpha( *(p+1), encoding )
++ || *(p+1) == '_' )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Element\n" );
++ #endif
++ returnNode = new TiXmlElement( "" );
++ }
++ else
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Unknown(2)\n" );
++ #endif
++ returnNode = new TiXmlUnknown();
++ }
++
++ if ( returnNode )
++ {
++ // Set the parent, so it can report errors
++ returnNode->parent = this;
++ }
++ else
++ {
++ if ( doc )
++ doc->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ }
++ return returnNode;
++}
++
++#ifdef TIXML_USE_STL
++
++void TiXmlElement::StreamIn (TIXML_ISTREAM * in, TIXML_STRING * tag)
++{
++ // We're called with some amount of pre-parsing. That is, some of "this"
++ // element is in "tag". Go ahead and stream to the closing ">"
++ while( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ (*tag) += (char) c ;
++
++ if ( c == '>' )
++ break;
++ }
++
++ if ( tag->length() < 3 ) return;
++
++ // Okay...if we are a "/>" tag, then we're done. We've read a complete tag.
++ // If not, identify and stream.
++
++ if ( tag->at( tag->length() - 1 ) == '>'
++ && tag->at( tag->length() - 2 ) == '/' )
++ {
++ // All good!
++ return;
++ }
++ else if ( tag->at( tag->length() - 1 ) == '>' )
++ {
++ // There is more. Could be:
++ // text
++ // closing tag
++ // another node.
++ for ( ;; )
++ {
++ StreamWhiteSpace( in, tag );
++
++ // Do we have text?
++ if ( in->good() && in->peek() != '<' )
++ {
++ // Yep, text.
++ TiXmlText text( "" );
++ text.StreamIn( in, tag );
++
++ // What follows text is a closing tag or another node.
++ // Go around again and figure it out.
++ continue;
++ }
++
++ // We now have either a closing tag...or another node.
++ // We should be at a "<", regardless.
++ if ( !in->good() ) return;
++ assert( in->peek() == '<' );
++ int tagIndex = tag->length();
++
++ bool closingTag = false;
++ bool firstCharFound = false;
++
++ for( ;; )
++ {
++ if ( !in->good() )
++ return;
++
++ int c = in->peek();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ if ( c == '>' )
++ break;
++
++ *tag += (char) c;
++ in->get();
++
++ if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) )
++ {
++ firstCharFound = true;
++ if ( c == '/' )
++ closingTag = true;
++ }
++ }
++ // If it was a closing tag, then read in the closing '>' to clean up the input stream.
++ // If it was not, the streaming will be done by the tag.
++ if ( closingTag )
++ {
++ if ( !in->good() )
++ return;
++
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ assert( c == '>' );
++ *tag += (char) c;
++
++ // We are done, once we've found our closing tag.
++ return;
++ }
++ else
++ {
++ // If not a closing tag, id it, and stream.
++ const char* tagloc = tag->c_str() + tagIndex;
++ TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING );
++ if ( !node )
++ return;
++ node->StreamIn( in, tag );
++ delete node;
++ node = 0;
++
++ // No return: go around from the beginning: text, closing tag, or node.
++ }
++ }
++ }
++}
++#endif
++
++const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ p = SkipWhiteSpace( p, encoding );
++ TiXmlDocument* document = GetDocument();
++
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
++ return 0;
++ }
++
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++
++ if ( *p != '<' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
++ return 0;
++ }
++
++ p = SkipWhiteSpace( p+1, encoding );
++
++ // Read the name.
++ const char* pErr = p;
++
++ p = ReadName( p, &value, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
++ return 0;
++ }
++
++ TIXML_STRING endTag ("</");
++ endTag += value;
++ endTag += ">";
++
++ // Check for and read attributes. Also look for an empty
++ // tag or an end tag.
++ while ( p && *p )
++ {
++ pErr = p;
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
++ return 0;
++ }
++ if ( *p == '/' )
++ {
++ ++p;
++ // Empty tag.
++ if ( *p != '>' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );
++ return 0;
++ }
++ return (p+1);
++ }
++ else if ( *p == '>' )
++ {
++ // Done with attributes (if there were any.)
++ // Read the value -- which can include other
++ // elements -- read the end tag, and return.
++ ++p;
++ p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens.
++ if ( !p || !*p )
++ return 0;
++
++ // We should find the end tag now
++ if ( StringEqual( p, endTag.c_str(), false, encoding ) )
++ {
++ p += endTag.length();
++ return p;
++ }
++ else
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
++ return 0;
++ }
++ }
++ else
++ {
++ // Try to read an attribute:
++ TiXmlAttribute* attrib = new TiXmlAttribute();
++ if ( !attrib )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, pErr, data, encoding );
++ return 0;
++ }
++
++ attrib->SetDocument( document );
++ const char* pErr = p;
++ p = attrib->Parse( p, data, encoding );
++
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
++ delete attrib;
++ return 0;
++ }
++
++ // Handle the strange case of double attributes:
++ TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
++ if ( node )
++ {
++ node->SetValue( attrib->Value() );
++ delete attrib;
++ return 0;
++ }
++
++ attributeSet.Add( attrib );
++ }
++ }
++ return p;
++}
++
++
++const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ TiXmlDocument* document = GetDocument();
++
++ const char* pWithWhiteSpace = p;
++ // Read in text and elements in any order.
++ p = SkipWhiteSpace( p, encoding );
++ while ( p && *p )
++ {
++ if ( *p != '<' )
++ {
++ // Take what we have, make a text element.
++ TiXmlText* textNode = new TiXmlText( "" );
++
++ if ( !textNode )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, encoding );
++ return 0;
++ }
++
++ if ( TiXmlBase::IsWhiteSpaceCondensed() )
++ {
++ p = textNode->Parse( p, data, encoding );
++ }
++ else
++ {
++ // Special case: we want to keep the white space
++ // so that leading spaces aren't removed.
++ p = textNode->Parse( pWithWhiteSpace, data, encoding );
++ }
++
++ if ( !textNode->Blank() )
++ LinkEndChild( textNode );
++ else
++ delete textNode;
++ }
++ else
++ {
++ // We hit a '<'
++ // Have we hit a new element or an end tag?
++ if ( StringEqual( p, "</", false, encoding ) )
++ {
++ return p;
++ }
++ else
++ {
++ TiXmlNode* node = Identify( p, encoding );
++ if ( node )
++ {
++ p = node->Parse( p, data, encoding );
++ LinkEndChild( node );
++ }
++ else
++ {
++ return 0;
++ }
++ }
++ }
++ p = SkipWhiteSpace( p, encoding );
++ }
++
++ if ( !p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding );
++ }
++ return p;
++}
++
++
++#ifdef TIXML_USE_STL
++void TiXmlUnknown::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ (*tag) += (char) c;
++
++ if ( c == '>' )
++ {
++ // All is well.
++ return;
++ }
++ }
++}
++#endif
++
++
++const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ TiXmlDocument* document = GetDocument();
++ p = SkipWhiteSpace( p, encoding );
++
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++ if ( !p || !*p || *p != '<' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding );
++ return 0;
++ }
++ ++p;
++ value = "";
++
++ while ( p && *p && *p != '>' )
++ {
++ value += *p;
++ ++p;
++ }
++
++ if ( !p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding );
++ }
++ if ( *p == '>' )
++ return p+1;
++ return p;
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlComment::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ (*tag) += (char) c;
++
++ if ( c == '>'
++ && tag->at( tag->length() - 2 ) == '-'
++ && tag->at( tag->length() - 3 ) == '-' )
++ {
++ // All is well.
++ return;
++ }
++ }
++}
++#endif
++
++
++const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ TiXmlDocument* document = GetDocument();
++ value = "";
++
++ p = SkipWhiteSpace( p, encoding );
++
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++ const char* startTag = "<!--";
++ const char* endTag = "-->";
++
++ if ( !StringEqual( p, startTag, false, encoding ) )
++ {
++ document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding );
++ return 0;
++ }
++ p += strlen( startTag );
++ p = ReadText( p, &value, false, endTag, false, encoding );
++ return p;
++}
++
++
++const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p ) return 0;
++
++ int tabsize = 4;
++ if ( document )
++ tabsize = document->TabSize();
++
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++ // Read the name, the '=' and the value.
++ const char* pErr = p;
++ p = ReadName( p, &name, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
++ return 0;
++ }
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p || *p != '=' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
++ return 0;
++ }
++
++ ++p; // skip '='
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
++ return 0;
++ }
++
++ const char* end;
++
++ if ( *p == '\'' )
++ {
++ ++p;
++ end = "\'";
++ p = ReadText( p, &value, false, end, false, encoding );
++ }
++ else if ( *p == '"' )
++ {
++ ++p;
++ end = "\"";
++ p = ReadText( p, &value, false, end, false, encoding );
++ }
++ else
++ {
++ // All attribute values should be in single or double quotes.
++ // But this is such a common error that the parser will try
++ // its best, even without them.
++ value = "";
++ while ( p && *p // existence
++ && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace
++ && *p != '/' && *p != '>' ) // tag end
++ {
++ value += *p;
++ ++p;
++ }
++ }
++ return p;
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlText::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->peek();
++ if ( c == '<' )
++ return;
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ (*tag) += (char) c;
++ in->get();
++ }
++}
++#endif
++
++const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ value = "";
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++ bool ignoreWhite = true;
++
++ const char* end = "<";
++ p = ReadText( p, &value, ignoreWhite, end, false, encoding );
++ if ( p )
++ return p-1; // don't truncate the '<'
++ return 0;
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlDeclaration::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ (*tag) += (char) c;
++
++ if ( c == '>' )
++ {
++ // All is well.
++ return;
++ }
++ }
++}
++#endif
++
++const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding )
++{
++ p = SkipWhiteSpace( p, _encoding );
++ // Find the beginning, find the end, and look for
++ // the stuff in-between.
++ TiXmlDocument* document = GetDocument();
++ if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding );
++ return 0;
++ }
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, _encoding );
++ location = data->Cursor();
++ }
++ p += 5;
++
++ version = "";
++ encoding = "";
++ standalone = "";
++
++ while ( p && *p )
++ {
++ if ( *p == '>' )
++ {
++ ++p;
++ return p;
++ }
++
++ p = SkipWhiteSpace( p, _encoding );
++ if ( StringEqual( p, "version", true, _encoding ) )
++ {
++ TiXmlAttribute attrib;
++ p = attrib.Parse( p, data, _encoding );
++ version = attrib.Value();
++ }
++ else if ( StringEqual( p, "encoding", true, _encoding ) )
++ {
++ TiXmlAttribute attrib;
++ p = attrib.Parse( p, data, _encoding );
++ encoding = attrib.Value();
++ }
++ else if ( StringEqual( p, "standalone", true, _encoding ) )
++ {
++ TiXmlAttribute attrib;
++ p = attrib.Parse( p, data, _encoding );
++ standalone = attrib.Value();
++ }
++ else
++ {
++ // Read over whatever it is.
++ while( p && *p && *p != '>' && !IsWhiteSpace( *p ) )
++ ++p;
++ }
++ }
++ return 0;
++}
++
++bool TiXmlText::Blank() const
++{
++ for ( unsigned i=0; i<value.length(); i++ )
++ if ( !IsWhiteSpace( value[i] ) )
++ return false;
++ return true;
++}
++#endif /* SETUP */
+diff -ruNp vdr-1.6.0-2/transfer.c vdr-1.6.0-2-extensions/transfer.c
+--- vdr-1.6.0-2/transfer.c 2007-01-05 11:45:28.000000000 +0100
++++ vdr-1.6.0-2-extensions/transfer.c 2009-04-09 20:48:27.000000000 +0200
+@@ -19,7 +19,11 @@ cTransfer::cTransfer(tChannelID ChannelI
+ ,cThread("transfer")
+ {
+ ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
++#ifdef USE_SYNCEARLY
++ remux = new cRemux(VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids, false, Setup.UseSyncEarlyPatch);
++#else
+ remux = new cRemux(VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids);
++#endif /* SYNCEARLY */
+ }
+
+ cTransfer::~cTransfer()
+diff -ruNp vdr-1.6.0-2/transfer.h vdr-1.6.0-2-extensions/transfer.h
+--- vdr-1.6.0-2/transfer.h 2007-01-05 11:45:45.000000000 +0100
++++ vdr-1.6.0-2-extensions/transfer.h 2009-04-09 20:48:27.000000000 +0200
+@@ -30,6 +30,10 @@ public:
+ };
+
+ class cTransferControl : public cControl {
++#ifdef USE_LIVEBUFFER
++friend class cLiveBufferControl;
++friend class cLiveBufferManager;
++#endif /* LIVEBUFFER */
+ private:
+ cTransfer *transfer;
+ static cDevice *receiverDevice;
+diff -ruNp vdr-1.6.0-2/vdr.c vdr-1.6.0-2-extensions/vdr.c
+--- vdr-1.6.0-2/vdr.c 2008-09-11 13:29:43.000000000 +0200
++++ vdr-1.6.0-2-extensions/vdr.c 2009-04-09 20:48:27.000000000 +0200
+@@ -50,6 +50,9 @@
+ #include "keys.h"
+ #include "libsi/si.h"
+ #include "lirc.h"
++#ifdef USE_LIVEBUFFER
++#include "livebuffer.h"
++#endif /* LIVEBUFFER */
+ #include "menu.h"
+ #include "osdbase.h"
+ #include "plugin.h"
+@@ -139,10 +142,17 @@ static bool SetKeepCaps(bool On)
+ return true;
+ }
+
++#ifdef USE_SETTIME
++char *SetTime = NULL;
++#endif /* SETTIME */
++
+ static void SignalHandler(int signum)
+ {
+ switch (signum) {
+ case SIGPIPE:
++#ifdef USE_EM84XX
++ case SIGINT:
++#endif /* EM84XX */
+ break;
+ case SIGHUP:
+ LastSignal = signum;
+@@ -163,6 +173,10 @@ static void Watchdog(int signum)
+ exit(1);
+ }
+
++#ifdef USE_CMDRECCMDI18N
++const char *ConfigDirectory = NULL;
++#endif /* CMDRECCMDI18N */
++
+ int main(int argc, char *argv[])
+ {
+ // Save terminal settings:
+@@ -187,8 +201,11 @@ int main(int argc, char *argv[])
+ bool UserDump = false;
+ int SVDRPport = DEFAULTSVDRPPORT;
+ const char *AudioCommand = NULL;
++#ifndef USE_CMDRECCMDI18N
+ const char *ConfigDirectory = NULL;
++#endif /* CMDRECCMDI18N */
+ const char *EpgDataFileName = DEFAULTEPGDATAFILENAME;
++ bool DisplayExtensions = false;
+ bool DisplayHelp = false;
+ bool DisplayVersion = false;
+ bool DaemonMode = false;
+@@ -220,10 +237,14 @@ int main(int argc, char *argv[])
+
+ static struct option long_options[] = {
+ { "audio", required_argument, NULL, 'a' },
++#ifdef USE_LIVEBUFFER
++ { "buffer", required_argument, NULL, 'b' },
++#endif /* LIVEBUFFER */
+ { "config", required_argument, NULL, 'c' },
+ { "daemon", no_argument, NULL, 'd' },
+ { "device", required_argument, NULL, 'D' },
+ { "epgfile", required_argument, NULL, 'E' },
++ { "extensions",no_argument, NULL, 'e' | 0x100 },
+ { "grab", required_argument, NULL, 'g' },
+ { "help", no_argument, NULL, 'h' },
+ { "lib", required_argument, NULL, 'L' },
+@@ -238,6 +259,9 @@ int main(int argc, char *argv[])
+ { "record", required_argument, NULL, 'r' },
+ { "shutdown", required_argument, NULL, 's' },
+ { "terminal", required_argument, NULL, 't' },
++#ifdef USE_SETTIME
++ { "timeset", required_argument, NULL, 'T' },
++#endif /* SETTIME */
+ { "user", required_argument, NULL, 'u' },
+ { "userdump", no_argument, NULL, 'u' | 0x100 },
+ { "version", no_argument, NULL, 'V' },
+@@ -248,10 +272,28 @@ int main(int argc, char *argv[])
+ };
+
+ int c;
++#ifdef USE_SETTIME
++#ifdef USE_LIVEBUFFER
++ while ((c = getopt_long(argc, argv, "a::b:c:dD:E:g:hl:L:mp:P:r:s:t:T:u:v:Vw:", long_options, NULL)) != -1) {
++#else
++ while ((c = getopt_long(argc, argv, "a:c:dD:E:g:hl:L:mp:P:r:s:t:T:u:v:Vw:", long_options, NULL)) != -1) {
++#endif /* LIVEBUFFER */
++#else
++#ifdef USE_LIVEBUFFER
++ while ((c = getopt_long(argc, argv, "a::b:c:dD:E:g:hl:L:mp:P:r:s:t:u:v:Vw:", long_options, NULL)) != -1) {
++#else
+ while ((c = getopt_long(argc, argv, "a:c:dD:E:g:hl:L:mp:P:r:s:t:u:v:Vw:", long_options, NULL)) != -1) {
++#endif /* LIVEBUFFER */
++#endif /* SETTIME */
+ switch (c) {
+ case 'a': AudioCommand = optarg;
+ break;
++#ifdef USE_LIVEBUFFER
++ case 'b': BufferDirectory = optarg;
++ while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/')
++ optarg[strlen(optarg) - 1] = 0;
++ break;
++#endif /* LIVEBUFFER */
+ case 'c': ConfigDirectory = optarg;
+ break;
+ case 'd': DaemonMode = true; break;
+@@ -265,6 +307,9 @@ int main(int argc, char *argv[])
+ fprintf(stderr, "vdr: invalid DVB device number: %s\n", optarg);
+ return 2;
+ break;
++ case 'e' | 0x100:
++ DisplayExtensions = true;
++ break;
+ case 'E': EpgDataFileName = (*optarg != '-' ? optarg : NULL);
+ break;
+ case 'g': cSVDRP::SetGrabImageDir(*optarg != '-' ? optarg : NULL);
+@@ -336,6 +381,10 @@ int main(int argc, char *argv[])
+ break;
+ case 's': ShutdownHandler.SetShutdownCommand(optarg);
+ break;
++#ifdef USE_SETTIME
++ case 'T': SetTime = strdup(optarg);
++ break;
++#endif /* SETTIME */
+ case 't': Terminal = optarg;
+ if (access(Terminal, R_OK | W_OK) < 0) {
+ fprintf(stderr, "vdr: can't access terminal: %s\n", Terminal);
+@@ -396,11 +445,16 @@ int main(int argc, char *argv[])
+ if (DisplayHelp) {
+ printf("Usage: vdr [OPTIONS]\n\n" // for easier orientation, this is column 80|
+ " -a CMD, --audio=CMD send Dolby Digital audio to stdin of command CMD\n"
++#ifdef USE_LIVEBUFFER
++ " -b DIR, --buffer=DIR use DIR as LiveBuffer directory (default is to write\n"
++ " it to the video directory)\n"
++#endif /* LIVEBUFFER */
+ " -c DIR, --config=DIR read config files from DIR (default: %s)\n"
+ " -d, --daemon run in daemon mode\n"
+ " -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n"
+ " there may be several -D options (default: all DVB\n"
+ " devices will be used)\n"
++ " --extensions print patchlevel and exit\n"
+ " -E FILE, --epgfile=FILE write the EPG data into the given FILE (default is\n"
+ " '%s' in the video directory)\n"
+ " '-E-' disables this\n"
+@@ -431,6 +485,9 @@ int main(int argc, char *argv[])
+ " -r CMD, --record=CMD call CMD before and after a recording\n"
+ " -s CMD, --shutdown=CMD call CMD to shutdown the computer\n"
+ " -t TTY, --terminal=TTY controlling tty\n"
++#ifdef USE_SETTIME
++ " -T CMD, --timeset=CMD call CMD to set the system time\n"
++#endif /* SETTIME */
+ " -u USER, --user=USER run as user USER; only applicable if started as\n"
+ " root\n"
+ " --userdump allow coredumps if -u is given (debugging)\n"
+@@ -474,6 +531,213 @@ int main(int argc, char *argv[])
+ return 0;
+ }
+
++ if (DisplayExtensions) {
++ printf("VDR %s\n", VDRVERSION);
++ printf("VDREXTENSIONS %d\n", VDREXTENSIONS);
++
++#ifdef USE_ANALOGTV
++ printf(" ANALOGTV\n");
++#endif /* ANALOGTV */
++
++#ifdef USE_ATSC
++ printf(" ATSC\n");
++#endif /* ATSC */
++
++#ifdef USE_CHANNELSCAN
++ printf(" CHANNELSCAN\n");
++#endif /* CHANNELSCAN */
++
++#ifdef USE_CMDRECCMDI18N
++ printf(" CMDRECCMDI18N\n");
++#endif /* CMDRECCMDI18N */
++
++#ifdef USE_CMDSUBMENU
++ printf(" CMDSUBMENU %d\n", CMDSUBMENUVERSNUM);
++#endif /* CMDSUBMENU */
++
++#ifdef USE_CUTTERLIMIT
++ printf(" CUTTERLIMIT\n");
++#endif /* CUTTERLIMIT */
++
++#ifdef USE_CUTTERQUEUE
++ printf(" CUTTERQUEUE\n");
++#endif /* CUTTERQUEUE */
++
++#ifdef USE_CUTTIME
++ printf(" CUTTIME\n");
++#endif /* CUTTIME */
++
++#ifdef USE_DDEPGENTRY
++ printf(" DDEPGENTRY\n");
++#endif /* DDEPGENTRY */
++
++#ifdef USE_DELTIMESHIFTREC
++ printf(" DELTIMESHIFTREC\n");
++#endif /* DELTIMESHIFTREC */
++
++#ifdef USE_DOLBYINREC
++ printf(" DOLBYINREC\n");
++#endif /* DOLBYINREC */
++
++#ifdef USE_DVBPLAYER
++ printf(" DVBPLAYER\n");
++#endif /* DVBPLAYER */
++
++#ifdef USE_DVBSETUP
++ printf(" DVBSETUP\n");
++#endif /* DVBSETUP */
++
++#ifdef USE_DVDARCHIVE
++ printf(" DVDARCHIVE\n");
++#endif /* DVDARCHIVE */
++
++#ifdef USE_DVDCHAPJUMP
++ printf(" DVDCHAPJUMP\n");
++#endif /* DVDCHAPJUMP */
++
++#ifdef USE_DVLRECSCRIPTADDON
++ printf(" DVLRECSCRIPTADDON\n");
++#endif /* DVLRECSCRIPTADDON */
++
++#ifdef USE_DVLVIDPREFER
++ printf(" DVLVIDPREFER\n");
++#endif /* DVLVIDPREFER */
++
++#ifdef USE_DVLFRIENDLYFNAMES
++ printf(" DVLFRIENDLYFNAMES\n");
++#endif /* DVLFRIENDLYFNAMES */
++
++#ifdef USE_EM84XX
++ printf(" EM84XX\n");
++#endif /* EM84XX */
++
++#ifdef USE_GRAPHTFT
++ printf(" GRAPHTFT\n");
++#endif /* GRAPHTFT */
++
++#ifdef USE_HARDLINKCUTTER
++ printf(" HARDLINKCUTTER\n");
++#endif /* HARDLINKCUTTER */
++
++#ifdef USE_JUMPPLAY
++ printf(" JUMPPLAY %d\n", JUMPPLAYVERSNUM);
++#endif /* JUMPPLAY */
++
++#ifdef USE_LIEMIEXT
++ printf(" LIEMIEXT %d\n", LIEMIKUUTIO);
++#endif /* LIEMIEXT */
++
++#ifdef USE_LIRCSETTINGS
++ printf(" LIRCSETTINGS\n");
++#endif /* LIRCSETTINGS */
++
++#ifdef USE_LIVEBUFFER
++ printf(" LIVEBUFFER\n");
++#endif /* LIVEBUFFER */
++
++#ifdef USE_LNBSHARE
++ printf(" LNBSHARE\n");
++#endif /* LNBSHARE */
++
++#ifdef USE_MAINMENUHOOKS
++ printf(" MAINMENUHOOKS %3.1f\n", MAINMENUHOOKSVERSNUM);
++#endif /* MAINMENUHOOKS */
++
++#ifdef USE_MENUORG
++ printf(" MENUORG\n");
++#endif /* MENUORG */
++
++#ifdef USE_NOEPG
++ printf(" NOEPG\n");
++#endif /* NOEPG */
++
++#ifdef USE_OSDMAXITEMS
++ printf(" OSDMAXITEMS\n");
++#endif /* OSDMAXITEMS */
++
++#ifdef USE_PARENTALRATING
++ printf(" PARENTALRATING\n");
++#endif /* PARENTALRATING */
++
++#ifdef USE_PINPLUGIN
++ printf(" PINPLUGIN %d\n", PIN_PLUGIN_PATCH);
++#endif /* PINPLUGIN */
++
++#ifdef USE_PLUGINAPI
++ printf(" PLUGINAPI\n");
++#endif /* PLUGINAPI */
++
++#ifdef USE_PLUGINMISSING
++ printf(" PLUGINMISSING\n");
++#endif /* PLUGINMISSING */
++
++#ifdef USE_PLUGINPARAM
++ printf(" PLUGINPARAM %d\n", PLUGINPARAMPATCHVERSNUM);
++#endif /* PLUGINPARAM */
++
++#ifdef USE_ROTOR
++ printf(" ROTOR\n");
++#endif /* ROTOR */
++
++#ifdef USE_SETTIME
++ printf(" SETTIME\n");
++#endif /* SETTIME */
++
++#ifdef USE_SETUP
++ printf(" SETUP\n");
++#endif /* SETUP */
++
++#ifdef USE_SOFTOSD
++ printf(" SOFTOSD\n");
++#endif /* SOFTOSD */
++
++#ifdef USE_SOURCECAPS
++ printf(" SOURCECAPS\n");
++#endif /* SOURCECAPS */
++
++#ifdef USE_SORTRECORDS
++ printf(" SORTRECORDS\n");
++#endif /* SORTRECORDS */
++
++#ifdef USE_STREAMDEVEXT
++ printf(" STREAMDEVEXT\n");
++#endif /* STREAMDEVEXT */
++
++#ifdef USE_SYNCEARLY
++ printf(" SYNCEARLY\n");
++#endif /* SYNCEARLY */
++
++#ifdef USE_TIMERCMD
++ printf(" TIMERCMD\n");
++#endif /* TIMERCMD */
++
++#ifdef USE_TIMERINFO
++ printf(" TIMERINFO\n");
++#endif /* TIMERINFO */
++
++#ifdef USE_TTXTSUBS
++ printf(" TTXTSUBS\n");
++#endif /* TTXTSUBS*/
++
++#ifdef USE_VALIDINPUT
++ printf(" VALIDINPUT\n");
++#endif /* VALIDINPUT */
++
++#ifdef USE_VOLCTRL
++ printf(" VOLCTRL\n");
++#endif /* VOLCTRL */
++
++#ifdef USE_WAREAGLEICON
++ printf(" WAREAGLEICON\n");
++#endif /* WAREAGLEICON */
++
++#ifdef USE_YAEPG
++ printf(" YAEPG\n");
++#endif /* YAEPG */
++
++ return 0;
++ }
++
+ // Log file:
+
+ if (SysLogLevel > 0)
+@@ -553,6 +817,11 @@ int main(int argc, char *argv[])
+ if (!PluginManager.LoadPlugins(true))
+ EXIT(2);
+
++#ifdef USE_LIVEBUFFER
++ if (!BufferDirectory)
++ BufferDirectory = VideoDirectory;
++#endif /* LIVEBUFFER */
++
+ // Configuration data:
+
+ if (!ConfigDirectory)
+@@ -566,8 +835,20 @@ int main(int argc, char *argv[])
+ Diseqcs.Load(AddDirectory(ConfigDirectory, "diseqc.conf"), true, Setup.DiSEqC) &&
+ Channels.Load(AddDirectory(ConfigDirectory, "channels.conf"), false, true) &&
+ Timers.Load(AddDirectory(ConfigDirectory, "timers.conf")) &&
++#ifdef USE_CMDRECCMDI18N
++ LoadCommandsI18n(Commands,AddDirectory(ConfigDirectory, "commands.conf"), true) &&
++ LoadCommandsI18n(RecordingCommands, AddDirectory(ConfigDirectory, "reccmds.conf"), true) &&
++#else
+ Commands.Load(AddDirectory(ConfigDirectory, "commands.conf"), true) &&
+ RecordingCommands.Load(AddDirectory(ConfigDirectory, "reccmds.conf"), true) &&
++#endif /* CMDRECCMDI18N */
++#ifdef USE_TIMERCMD
++#ifdef USE_CMDRECCMDI18N
++ LoadCommandsI18n(TimerCommands, AddDirectory(ConfigDirectory, "timercmds.conf"), true) &&
++#else
++ TimerCommands.Load(AddDirectory(ConfigDirectory, "timercmds.conf"), true) &&
++#endif /* CMDRECCMDI18N */
++#endif /* TIMERCMD */
+ SVDRPhosts.Load(AddDirectory(ConfigDirectory, "svdrphosts.conf"), true) &&
+ Keys.Load(AddDirectory(ConfigDirectory, "remote.conf")) &&
+ KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true)
+@@ -730,7 +1011,11 @@ int main(int argc, char *argv[])
+ // Make sure we have a visible programme in case device usage has changed:
+ if (!EITScanner.Active() && cDevice::PrimaryDevice()->HasDecoder() && !cDevice::PrimaryDevice()->HasProgramme()) {
+ static time_t lastTime = 0;
++#ifdef USE_CHANNELSCAN
++ if (!scanning_on_receiving_device && (!Menu || CheckHasProgramme) && Now - lastTime > MINCHANNELWAIT) {
++#else
+ if ((!Menu || CheckHasProgramme) && Now - lastTime > MINCHANNELWAIT) { // !Menu to avoid interfering with the CAM if a CAM menu is open
++#endif /* CHANNELSCAN */
+ cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel());
+ if (Channel && (Channel->Vpid() || Channel->Apid(0))) {
+ if (!Channels.SwitchTo(cDevice::CurrentChannel()) // try to switch to the original channel...
+@@ -786,8 +1071,17 @@ int main(int argc, char *argv[])
+ }
+ // Channel display:
+ if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) {
++#ifdef USE_LIVEBUFFER
++ if (!Menu) {
++ if (cControl::Control(true))
++ cControl::Control(true)->Hide();
++#else
+ if (!Menu)
++#endif /* LIVEBUFFER */
+ Menu = new cDisplayChannel(cDevice::CurrentChannel(), LastChannel >= 0);
++#ifdef USE_LIVEBUFFER
++ }
++#endif /* LIVEBUFFER */
+ LastChannel = cDevice::CurrentChannel();
+ LastChannelChanged = Now;
+ }
+@@ -902,9 +1196,19 @@ int main(int argc, char *argv[])
+ if (!Skins.IsOpen())
+ Skins.ProcessQueuedMessages();
+ // User Input:
++#ifdef USE_LIVEBUFFER
++ cOsdObject *Interact = Menu ? Menu : cControl::Control(true);
++#else
+ cOsdObject *Interact = Menu ? Menu : cControl::Control();
++#endif /* LIVEBUFFER */
+ eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
++#ifdef USE_LIVEBUFFER
++ Interact = Menu ? Menu : cControl::Control();
++#endif /* LIVEBUFFER */
+ if (ISREALKEY(key)) {
++#ifdef USE_PINPLUGIN
++ cStatus::MsgUserAction(key, Interact);
++#endif /* PINPLUGIN */
+ EITScanner.Activity();
+ // Cancel shutdown countdown:
+ if (ShutdownHandler.countdown)
+@@ -921,9 +1225,17 @@ int main(int argc, char *argv[])
+ bool WasMenu = Interact && Interact->IsMenu();
+ if (Menu)
+ DELETE_MENU;
++#ifdef USE_LIVEBUFFER
++ else if (cControl::Control(true)) {
++#else
+ else if (cControl::Control()) {
++#endif /* LIVEBUFFER */
+ if (cOsd::IsOpen())
++#ifdef USE_LIVEBUFFER
++ cControl::Control(true)->Hide();
++#else
+ cControl::Control()->Hide();
++#endif /* LIVEBUFFER */
+ else
+ WasOpen = false;
+ }
+@@ -956,12 +1268,21 @@ int main(int argc, char *argv[])
+ }
+ break;
+ // Direct main menu functions:
++#ifdef USE_LIVEBUFFER
++ #define DirectMainFunction(function)\
++ DELETE_MENU;\
++ if (cControl::Control(true))\
++ cControl::Control(true)->Hide();\
++ Menu = new cMenuMain(function);\
++ key = kNone; // nobody else needs to see this key
++#else
+ #define DirectMainFunction(function)\
+ DELETE_MENU;\
+ if (cControl::Control())\
+ cControl::Control()->Hide();\
+ Menu = new cMenuMain(function);\
+ key = kNone; // nobody else needs to see this key
++#endif /* LIVEBUFFER */
+ case kSchedule: DirectMainFunction(osSchedule); break;
+ case kChannels: DirectMainFunction(osChannels); break;
+ case kTimers: DirectMainFunction(osTimers); break;
+@@ -973,14 +1294,25 @@ int main(int argc, char *argv[])
+ const char *PluginName = cRemote::GetPlugin();
+ if (PluginName) {
+ DELETE_MENU;
++#ifdef USE_LIVEBUFFER
++ if (cControl::Control(true))
++ cControl::Control(true)->Hide();
++#else
+ if (cControl::Control())
+ cControl::Control()->Hide();
++#endif /* LIVEBUFFER */
+ cPlugin *plugin = cPluginManager::GetPlugin(PluginName);
+ if (plugin) {
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgPluginProtected(plugin)) {
++#endif /* PINPLUGIN */
+ Menu = plugin->MainMenuAction();
+ if (Menu)
+ Menu->Show();
+ }
++#ifdef USE_PINPLUGIN
++ }
++#endif /* PINPLUGIN */
+ else
+ esyslog("ERROR: unknown plugin '%s'", PluginName);
+ }
+@@ -992,8 +1324,17 @@ int main(int argc, char *argv[])
+ case kChanUp:
+ case kChanDn|k_Repeat:
+ case kChanDn:
++#ifdef USE_LIVEBUFFER
++ if (!Interact) {
++ if (cControl::Control(true))
++ cControl::Control(true)->Hide();
++#else
+ if (!Interact)
++#endif /* LIVEBUFFER */
+ Menu = new cDisplayChannel(NORMALKEY(key));
++#ifdef USE_LIVEBUFFER
++ }
++#endif /* LIVEBUFFER */
+ else if (cDisplayChannel::IsOpen() || cControl::Control()) {
+ Interact->ProcessKey(key);
+ continue;
+@@ -1023,8 +1364,13 @@ int main(int argc, char *argv[])
+ break;
+ // Audio track control:
+ case kAudio:
++#ifdef USE_LIVEBUFFER
++ if (cControl::Control(true))
++ cControl::Control(true)->Hide();
++#else
+ if (cControl::Control())
+ cControl::Control()->Hide();
++#endif /* LIVEBUFFER */
+ if (!cDisplayTracks::IsOpen()) {
+ DELETE_MENU;
+ Menu = cDisplayTracks::Create();
+@@ -1047,7 +1393,11 @@ int main(int argc, char *argv[])
+ break;
+ // Pausing live video:
+ case kPause:
++#ifdef USE_LIVEBUFFER
++ if (!cControl::Control() && !cLiveBufferManager::GetLiveBufferControl()) {
++#else
+ if (!cControl::Control()) {
++#endif /* LIVEBUFFER */
+ DELETE_MENU;
+ if (!cRecordControls::PauseLiveVideo())
+ Skins.Message(mtError, tr("No free DVB device to record!"));
+@@ -1056,9 +1406,20 @@ int main(int argc, char *argv[])
+ break;
+ // Instant recording:
+ case kRecord:
++#ifdef USE_LIVEBUFFER
++ if (!cControl::Control() && !cLiveBufferManager::GetLiveBufferControl()) {
++#else
+ if (!cControl::Control()) {
++#endif /* LIVEBUFFER */
++#ifdef USE_STREAMDEVEXT
++ if (cRecordControls::Start()) {
++ Skins.Message(mtInfo, tr("Recording started"));
++ cStatus::MsgRecordingChange(NULL, scAdd);
++ }
++#else
+ if (cRecordControls::Start())
+ Skins.Message(mtInfo, tr("Recording started"));
++#endif /* STREAMDEVEXT */
+ key = kNone; // nobody else needs to see this key
+ }
+ break;
+@@ -1108,14 +1469,42 @@ int main(int argc, char *argv[])
+ state = osEnd;
+ }
+ switch (state) {
++#ifdef USE_LIVEBUFFER
++ case osPause: if (cLiveBufferManager::GetLiveBufferControl()) {
++ DELETE_MENU;
++ if (cControl::Control(true)) {
++ cControl::Control(true)->ProcessKey(kPlay);
++ cControl::Control(true)->ProcessKey(kPause);
++ }
++ break;
++ }
++ DELETE_MENU;
++#else
+ case osPause: DELETE_MENU;
++#endif /* LIVEBUFFER */
+ cControl::Shutdown(); // just in case
+ if (!cRecordControls::PauseLiveVideo())
+ Skins.Message(mtError, tr("No free DVB device to record!"));
+ break;
++#ifdef USE_LIVEBUFFER
++ case osRecord: if (cLiveBufferManager::GetLiveBufferControl()) {
++ if (cControl::Control(true))
++ cControl::Control(true)->ProcessKey(kRecord);
++ break;
++ }
++ DELETE_MENU;
++#else
+ case osRecord: DELETE_MENU;
++#endif /* LIVEBUFFER */
++#ifdef USE_STREAMDEVEXT
++ if (cRecordControls::Start()) {
++ Skins.Message(mtInfo, tr("Recording started"));
++ cStatus::MsgRecordingChange(NULL, scAdd);
++ }
++#else
+ if (cRecordControls::Start())
+ Skins.Message(mtInfo, tr("Recording started"));
++#endif /* STREAMDEVEXT */
+ break;
+ case osRecordings:
+ DELETE_MENU;
+@@ -1152,6 +1541,12 @@ int main(int argc, char *argv[])
+ }
+ }
+ else {
++#ifdef USE_LIVEBUFFER
++ eOSState state = osUnknown;
++ if (cLiveBufferManager::GetLiveBufferControl())
++ state = cLiveBufferManager::GetLiveBufferControl()->ProcessKey(key);
++ if (state == osUnknown) {
++#endif /* LIVEBUFFER */
+ // Key functions in "normal" viewing mode:
+ if (key != kNone && KeyMacros.Get(key)) {
+ cRemote::PutMacro(key);
+@@ -1166,13 +1561,26 @@ int main(int argc, char *argv[])
+ Channels.SwitchTo(PreviousChannel[PreviousChannelIndex ^= 1]);
+ break;
+ }
++#ifdef USE_VOLCTRL
++ // Left/Right volume control
++#else
+ // Direct Channel Select:
+ case k1 ... k9:
+ // Left/Right rotates through channel groups:
++#endif /* VOLCTRL */
+ case kLeft|k_Repeat:
+ case kLeft:
+ case kRight|k_Repeat:
+ case kRight:
++#ifdef USE_VOLCTRL
++ if (Setup.LRVolumeControl && Setup.LRChannelGroups < 2) {
++ cRemote::Put(NORMALKEY(key) == kLeft ? kVolDn : kVolUp, true);
++ break;
++ }
++ // else fall through
++ // Direct Channel Select:
++ case k1 ... k9:
++#endif /* VOLCTRL */
+ // Previous/Next rotates through channel groups:
+ case kPrev|k_Repeat:
+ case kPrev:
+@@ -1183,6 +1591,10 @@ int main(int argc, char *argv[])
+ case kUp:
+ case kDown|k_Repeat:
+ case kDown:
++#ifdef USE_LIVEBUFFER
++ if (cControl::Control(true))
++ cControl::Control(true)->Hide();
++#endif /* LIVEBUFFER */
+ Menu = new cDisplayChannel(NORMALKEY(key));
+ break;
+ // Viewing Control:
+@@ -1190,13 +1602,22 @@ int main(int argc, char *argv[])
+ // Instant resume of the last viewed recording:
+ case kPlay:
+ if (cReplayControl::LastReplayed()) {
++#ifdef USE_PINPLUGIN
++ if (cStatus::MsgReplayProtected(0, cReplayControl::LastReplayed(), 0, false) == false) {
++#endif /* PINPLUGIN */
+ cControl::Shutdown();
+ cControl::Launch(new cReplayControl);
+ }
++#ifdef USE_PINPLUGIN
++ }
++#endif /* PINPLUGIN */
+ break;
+ default: break;
+ }
+ }
++#ifdef USE_LIVEBUFFER
++ }
++#endif /* LIVEBUFFER */
+ if (!Menu) {
+ if (!InhibitEpgScan)
+ EITScanner.Process();
+@@ -1266,6 +1687,9 @@ Exit:
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGALRM, SIG_DFL);
+
++#ifdef USE_LIVEBUFFER
++ cLiveBufferManager::Shutdown();
++#endif /* LIVEBUFFER */
+ PluginManager.StopPlugins();
+ cRecordControls::Shutdown();
+ cCutter::Stop();
+diff -ruNp vdr-1.6.0-2/vdrttxtsubshooks.c vdr-1.6.0-2-extensions/vdrttxtsubshooks.c
+--- vdr-1.6.0-2/vdrttxtsubshooks.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/vdrttxtsubshooks.c 2009-04-09 20:48:27.000000000 +0200
+@@ -0,0 +1,46 @@
++#ifdef USE_TTXTSUBS
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <stdint.h>
++
++#include "vdrttxtsubshooks.h"
++
++// XXX Really should be a list...
++static cVDRTtxtsubsHookListener *gListener;
++
++// ------ class cVDRTtxtsubsHookProxy ------
++
++class cVDRTtxtsubsHookProxy : public cVDRTtxtsubsHookListener
++{
++ public:
++ virtual void HideOSD(void) { if(gListener) gListener->HideOSD(); };
++ virtual void ShowOSD(void) { if(gListener) gListener->ShowOSD(); };
++ virtual void PlayerTeletextData(uint8_t *p, int length)
++ { if(gListener) gListener->PlayerTeletextData(p, length); };
++ virtual cTtxtSubsRecorderBase *NewTtxtSubsRecorder(cDevice *dev, const cChannel *ch)
++ { if(gListener) return gListener->NewTtxtSubsRecorder(dev, ch); else return NULL; };
++};
++
++
++// ------ class cVDRTtxtsubsHookListener ------
++
++cVDRTtxtsubsHookListener::~cVDRTtxtsubsHookListener()
++{
++ gListener = 0;
++}
++
++void cVDRTtxtsubsHookListener::HookAttach(void)
++{
++ gListener = this;
++ //printf("cVDRTtxtsubsHookListener::HookAttach\n");
++}
++
++static cVDRTtxtsubsHookProxy gProxy;
++
++cVDRTtxtsubsHookListener *cVDRTtxtsubsHookListener::Hook(void)
++{
++ return &gProxy;
++}
++
++#endif /* TTXTSUBS */
+diff -ruNp vdr-1.6.0-2/vdrttxtsubshooks.h vdr-1.6.0-2-extensions/vdrttxtsubshooks.h
+--- vdr-1.6.0-2/vdrttxtsubshooks.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.6.0-2-extensions/vdrttxtsubshooks.h 2009-04-09 20:48:27.000000000 +0200
+@@ -0,0 +1,38 @@
++#ifdef USE_TTXTSUBS
++
++#ifndef __VDRTTXTSUBSHOOKS_H
++#define __VDRTTXTSUBSHOOKS_H
++
++class cDevice;
++class cChannel;
++
++#define VDRTTXTSUBSHOOKS
++
++class cTtxtSubsRecorderBase {
++ public:
++ virtual ~cTtxtSubsRecorderBase() {};
++
++ // returns a PES packet if there is data to add to the recording
++ virtual uint8_t *GetPacket(uint8_t **buf, size_t *len) { return NULL; };
++ virtual void DeviceAttach(void) {};
++};
++
++class cVDRTtxtsubsHookListener {
++ public:
++ cVDRTtxtsubsHookListener(void) {};
++ virtual ~cVDRTtxtsubsHookListener();
++
++ void HookAttach(void);
++
++ virtual void HideOSD(void) {};
++ virtual void ShowOSD(void) {};
++ virtual void PlayerTeletextData(uint8_t *p, int length) {};
++ virtual cTtxtSubsRecorderBase *NewTtxtSubsRecorder(cDevice *dev, const cChannel *ch)
++ { return NULL; };
++
++ // used by VDR to call hook listeners
++ static cVDRTtxtsubsHookListener *Hook(void);
++};
++
++#endif /* __VDRTTXTSUBSHOOKS_H */
++#endif /* TTXTSUBS */
+diff -ruNp vdr-1.6.0-2/videodir.c vdr-1.6.0-2-extensions/videodir.c
+--- vdr-1.6.0-2/videodir.c 2008-02-16 14:00:03.000000000 +0100
++++ vdr-1.6.0-2-extensions/videodir.c 2009-04-09 20:48:27.000000000 +0200
+@@ -19,7 +19,17 @@
+ #include "recording.h"
+ #include "tools.h"
+
++#ifdef USE_HARDLINKCUTTER
++//#define HARDLINK_TEST_ONLY
++#ifdef USE_STREAMDEVEXT
++#include "status.h"
++#endif /* STREAMDEVEXT */
++#endif /* HARDLINKCUTTER */
++
+ const char *VideoDirectory = VIDEODIR;
++#ifdef USE_LIVEBUFFER
++const char *BufferDirectory = NULL;
++#endif /* LIVEBUFFER */
+
+ class cVideoDirectory {
+ private:
+@@ -36,6 +46,11 @@ public:
+ bool Next(void);
+ void Store(void);
+ const char *Adjust(const char *FileName);
++#ifdef USE_DVLVIDPREFER
++ char *GetVidPath(int nVid);
++ bool GetPreferedVideoDir(void);
++ bool IsVidDirOK(int nVid, int *freeMB = NULL);
++#endif /* DVLVIDPREFER */
+ };
+
+ cVideoDirectory::cVideoDirectory(void)
+@@ -117,6 +132,9 @@ cUnbufferedFile *OpenVideoFile(const cha
+ if ((Flags & O_CREAT) != 0) {
+ cVideoDirectory Dir;
+ if (Dir.IsDistributed()) {
++#ifdef USE_DVLVIDPREFER
++ if (Setup.UseVidPrefer == 0) {
++#endif /* DVLVIDPREFER */
+ // Find the directory with the most free space:
+ int MaxFree = Dir.FreeMB();
+ while (Dir.Next()) {
+@@ -126,14 +144,24 @@ cUnbufferedFile *OpenVideoFile(const cha
+ MaxFree = Free;
+ }
+ }
++#ifdef USE_DVLVIDPREFER
++ }
++ else Dir.GetPreferedVideoDir();
++#endif /* DVLVIDPREFER */
+ if (Dir.Stored()) {
+ ActualFileName = Dir.Adjust(FileName);
+ if (!MakeDirs(ActualFileName, false))
+ return NULL; // errno has been set by MakeDirs()
++#ifdef USE_DVLVIDPREFER
++ if (strcmp(ActualFileName, FileName) != 0) {
++#endif /* DVLVIDPREFER */
+ if (symlink(ActualFileName, FileName) < 0) {
+ LOG_ERROR_STR(FileName);
+ return NULL;
+ }
++#ifdef USE_DVLVIDPREFER
++ }
++#endif /* DVLVIDPREFER */
+ ActualFileName = strdup(ActualFileName); // must survive Dir!
+ }
+ }
+@@ -168,6 +196,125 @@ bool RemoveVideoFile(const char *FileNam
+ return RemoveFileOrDir(FileName, true);
+ }
+
++#ifdef USE_HARDLINKCUTTER
++static bool StatNearestDir(const char *FileName, struct stat *Stat)
++{
++ cString Name(FileName);
++ char *p;
++ while ((p = strrchr((const char*)Name + 1, '/')) != NULL) {
++ *p = 0; // truncate at last '/'
++ if (stat(Name, Stat) == 0) {
++ isyslog("StatNearestDir: Stating %s", (const char*)Name);
++ return true;
++ }
++ }
++ return false;
++}
++
++bool HardLinkVideoFile(const char *OldName, const char *NewName)
++{
++ // Incoming name must be in base video directory:
++ if (strstr(OldName, VideoDirectory) != OldName) {
++ esyslog("ERROR: %s not in %s", OldName, VideoDirectory);
++ return false;
++ }
++ if (strstr(NewName, VideoDirectory) != NewName) {
++ esyslog("ERROR: %s not in %s", NewName, VideoDirectory);
++ return false;
++ }
++
++ const char *ActualNewName = NewName;
++ cString ActualOldName(ReadLink(OldName), true);
++
++ // Some safety checks:
++ struct stat StatOldName;
++ if (lstat(ActualOldName, &StatOldName) == 0) {
++ if (S_ISLNK(StatOldName.st_mode)) {
++ esyslog("HardLinkVideoFile: Failed to resolve symbolic link %s", (const char*)ActualOldName);
++ return false;
++ }
++ }
++ else {
++ esyslog("HardLinkVideoFile: lstat failed on %s", (const char*)ActualOldName);
++ return false;
++ }
++ isyslog("HardLinkVideoFile: %s is on %i", (const char*)ActualOldName, (int)StatOldName.st_dev);
++
++ // Find the video directory where ActualOldName is located
++
++ cVideoDirectory Dir;
++ struct stat StatDir;
++ if (!StatNearestDir(NewName, &StatDir)) {
++ esyslog("HardLinkVideoFile: stat failed on %s", NewName);
++ return false;
++ }
++
++ isyslog("HardLinkVideoFile: %s is on %i", NewName, (int)StatDir.st_dev);
++ if (StatDir.st_dev != StatOldName.st_dev) {
++ // Not yet found.
++
++ if (!Dir.IsDistributed()) {
++ esyslog("HardLinkVideoFile: No matching video folder to hard link %s", (const char*)ActualOldName);
++ return false;
++ }
++
++ // Search in video01 and upwards
++ bool found = false;
++ while (Dir.Next()) {
++ Dir.Store();
++ const char *TmpNewName = Dir.Adjust(NewName);
++ if (StatNearestDir(TmpNewName, &StatDir) && StatDir.st_dev == StatOldName.st_dev) {
++ isyslog("HardLinkVideoFile: %s is on %i (match)", TmpNewName, (int)StatDir.st_dev);
++ ActualNewName = TmpNewName;
++ found = true;
++ break;
++ }
++ isyslog("HardLinkVideoFile: %s is on %i", TmpNewName, (int)StatDir.st_dev);
++ }
++ if (ActualNewName == NewName) {
++ esyslog("HardLinkVideoFile: No matching video folder to hard link %s", (const char*)ActualOldName);
++ return false;
++ }
++
++ // Looking good, we have a match. Create necessary folders.
++ if (!MakeDirs(ActualNewName, false))
++ return false;
++ // There's no guarantee that the directory of ActualNewName
++ // is on the same device as the dir that StatNearestDir found.
++ // But worst case is that the link fails.
++ }
++
++#ifdef HARDLINK_TEST_ONLY
++ // Do the hard link to *.vdr_ for testing only
++ char *name = NULL;
++ asprintf(&name, "%s_", ActualNewName);
++ link(ActualOldName, name);
++ free(name);
++ return false;
++#endif // HARDLINK_TEST_ONLY
++
++ // Try creating the hard link
++ if (link(ActualOldName, ActualNewName) != 0) {
++ // Failed to hard link. Maybe not allowed on file system.
++ LOG_ERROR_STR(ActualNewName);
++ isyslog("HardLinkVideoFile: failed to hard link from %s to %s", (const char*)ActualOldName, ActualNewName);
++ return false;
++ }
++
++ if (ActualNewName != NewName) {
++ // video01 and up. Do the remaining symlink
++ if (symlink(ActualNewName, NewName) < 0) {
++ LOG_ERROR_STR(NewName);
++ return false;
++ }
++ }
++#ifdef USE_STREAMDEVEXT
++ cStatus::MsgRecordingChange(Recordings.GetByName(NewName), scMod);
++#endif /* STREAMDEVEXT */
++ return true;
++}
++#endif /* HARDLINKCUTTER */
++
+ bool VideoFileSpaceAvailable(int SizeMB)
+ {
+ cVideoDirectory Dir;
+@@ -232,6 +379,129 @@ void RemoveEmptyVideoDirectories(void)
+ } while (Dir.Next());
+ }
+
++#ifdef USE_DVLVIDPREFER
++// returns path to nVid'th video directory or NULL if not existing
++char *cVideoDirectory::GetVidPath(int nVid)
++{
++ char *b = strdup(VideoDirectory);
++ int l = strlen(b), di, n;
++
++ while (l-- > 0 && isdigit(b[ l ]));
++
++ l++;
++ di = strlen(b) - l;
++
++ // di == number of digits
++ n = atoi(&b[ l ]);
++ if (n != 0)
++ return NULL;
++
++ // add requested number to dir name
++ sprintf(&b[ l ], "%0*d", di, nVid);
++
++ if (DirectoryOk(b) == true)
++ return b;
++
++ free(b);
++ return NULL;
++}
++
++// checks if a video dir is 'valid'
++bool cVideoDirectory::IsVidDirOK(int nVid, int *freeMB)
++{
++ char *dn;
++ int fMB;
++
++ if (nVid >= Setup.nVidPrefer)
++ return false;
++
++ if (Setup.VidPreferSize[ nVid ] == -1)
++ return false;
++
++ dn = GetVidPath(nVid);
++ if (dn == NULL)
++ return false;
++
++ fMB = FreeDiskSpaceMB(dn, NULL);
++ if (freeMB != NULL)
++ *freeMB = fMB;
++
++ free(dn);
++
++ if (Setup.VidPreferSize[ nVid ] >= fMB)
++ return false;
++ return true;
++}
++
++
++// calculates which video dir to use
++bool cVideoDirectory::GetPreferedVideoDir(void)
++{
++ cVideoDirectory d;
++ int nDirs = 1,
++ vidUse = Setup.nVidPrefer;
++ int i, top, topFree, x;
++
++ if (name == NULL)
++ return(false);
++
++ // count available video dirs
++ while (d.Next() == true)
++ nDirs++;
++
++ if (vidUse > nDirs)
++ vidUse = nDirs;
++
++ // check for prefered video dir
++ for (i = 0, top = -1, topFree = 0; i < vidUse; i++) {
++ if (IsVidDirOK(i, &x) == true) {
++ if (top == -1) {
++ // nothing set yet, use first 'ok' dir
++ top = i;
++ topFree = x;
++ }
++ else {
++ // check if we got a higher priority
++ if (Setup.VidPreferPrio[ i ] >= Setup.VidPreferPrio[ top ]) {
++ top = i;
++ topFree = x;
++ }
++ // check if we got same priority but more space
++ else if (Setup.VidPreferPrio[ i ] == Setup.VidPreferPrio[ top ] && x >= topFree) {
++ top = i;
++ topFree = x;
++ }
++ }
++ }
++ }
++
++ if (top == -1) {
++ isyslog("VidPrefer: no prefered video directory could be determined!");
++
++ // something went wrong here...
++ // let VDR determine the video directory
++ int MaxFree = FreeMB();
++
++ while (Next()) {
++ int Free = FreeDiskSpaceMB(Name());
++
++ if (Free > MaxFree) {
++ Store();
++ MaxFree = Free;
++ }
++ }
++ }
++ else {
++ isyslog("VidPrefer: prefered video directory '%d' set.", top);
++ if (stored != NULL)
++ free(stored);
++ stored = GetVidPath(top);
++ }
++
++ return true;
++}
++#endif /* DVLVIDPREFER */
++
+ bool IsOnVideoDirectoryFileSystem(const char *FileName)
+ {
+ cVideoDirectory Dir;
+diff -ruNp vdr-1.6.0-2/videodir.h vdr-1.6.0-2-extensions/videodir.h
+--- vdr-1.6.0-2/videodir.h 2008-02-16 13:53:11.000000000 +0100
++++ vdr-1.6.0-2-extensions/videodir.h 2009-04-09 20:48:27.000000000 +0200
+@@ -14,11 +14,17 @@
+ #include "tools.h"
+
+ extern const char *VideoDirectory;
++#ifdef USE_LIVEBUFFER
++extern const char *BufferDirectory;
++#endif /* LIVEBUFFER */
+
+ cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags);
+ int CloseVideoFile(cUnbufferedFile *File);
+ bool RenameVideoFile(const char *OldName, const char *NewName);
+ bool RemoveVideoFile(const char *FileName);
++#ifdef USE_HARDLINKCUTTER
++bool HardLinkVideoFile(const char *OldName, const char *NewName);
++#endif /* HARDLINKCUTTER */
+ bool VideoFileSpaceAvailable(int SizeMB);
+ int VideoDiskSpace(int *FreeMB = NULL, int *UsedMB = NULL); // returns the used disk space in percent
+ cString PrefixVideoFileName(const char *FileName, char Prefix);