summaryrefslogtreecommitdiff
path: root/vdr
diff options
context:
space:
mode:
authorroot <root@elwms02.(none)>2010-04-06 16:13:08 +0200
committerroot <root@elwms02.(none)>2010-04-06 16:13:08 +0200
commit0e7005fcc7483c01aa102fbea358c5ac65a48d62 (patch)
tree11517ce0d3d2977c6732b3aa583b0008083e0bd3 /vdr
downloadx-vdr-0e7005fcc7483c01aa102fbea358c5ac65a48d62.tar.gz
x-vdr-0e7005fcc7483c01aa102fbea358c5ac65a48d62.tar.bz2
hello world
Diffstat (limited to 'vdr')
-rw-r--r--vdr/conf/_xinitrc139
-rw-r--r--vdr/conf/channels.conf128
-rw-r--r--vdr/conf/channels.conf.dvb-c_unity-media168
-rw-r--r--vdr/conf/channels.conf.dvb-s_astra128
-rw-r--r--vdr/conf/keymacros.conf22
-rw-r--r--vdr/conf/noad-exclude-channels.conf53
-rw-r--r--vdr/conf/remote.conf180
-rw-r--r--vdr/conf/shutdown.conf68
-rw-r--r--vdr/extensions/vdr-1.6.0-2-ext_dvb-api.diff14
-rw-r--r--vdr/extensions/vdr-1.6.0-2_extensions.diff27399
-rw-r--r--vdr/extensions/vdr-1.7.0-ext_h264-s2ng-speedup.diff5483
-rw-r--r--vdr/extensions/vdr-1.7.0_extensions.diff28873
-rw-r--r--vdr/extensions/vdr-1.7.5_extensions.diff23830
-rw-r--r--vdr/install-vdr.sh987
-rw-r--r--vdr/make-conf.sh215
-rw-r--r--vdr/patchlevel.conf60
-rw-r--r--vdr/scripts/README.DE_burn-bc142
-rw-r--r--vdr/scripts/burn-bc838
-rw-r--r--vdr/scripts/runvdr247
-rw-r--r--vdr/scripts/runvdr-old319
-rw-r--r--vdr/scripts/timerrep.sh63
-rw-r--r--vdr/scripts/vdr130
-rw-r--r--vdr/scripts/vdr-no-shutdown12
-rw-r--r--vdr/scripts/vdr2root244
-rw-r--r--vdr/scripts/vdrmount35
-rw-r--r--vdr/scripts/vdrnoad99
-rw-r--r--vdr/scripts/vdrplayer279
-rw-r--r--vdr/scripts/vdrreaddvd31
-rw-r--r--vdr/scripts/vdrreccmds32
-rw-r--r--vdr/scripts/vdrsettime8
-rw-r--r--vdr/scripts/vdrsetup1896
-rw-r--r--vdr/scripts/vdrshutdown214
-rw-r--r--vdr/scripts/vdrwritedvd17
-rw-r--r--vdr/scripts/x-lcd26
-rw-r--r--vdr/vdr-1.7.5-ext_s2apiwrapper-0.5.diff475
35 files changed, 92854 insertions, 0 deletions
diff --git a/vdr/conf/_xinitrc b/vdr/conf/_xinitrc
new file mode 100644
index 0000000..4d9eab0
--- /dev/null
+++ b/vdr/conf/_xinitrc
@@ -0,0 +1,139 @@
+#!/bin/sh
+xset s off
+xset -dpms
+
+# xvdrplayer 0.0.1 (start-script for shmclient, tvtime, vdrtvtime, vdr-sxfe, xine or xineliboutput)
+# by Marc Wernecke - www.zulu-entertainment.de
+# 12.02.2009
+
+#VDRIP=127.0.0.1 # 127.0.0.1 for localhost or any other ip for a vdr-server in your local-network
+#VDRPLAYER=xine # shmclient, tvtime, vdr-sxfe, xine, xineliboutput or xine-network
+DIALOG="Xdialog"
+
+[ -d "$HOME" -a "$HOME" != "/" ] || HOME=/etc
+[ -r $HOME/vdrplayer.conf ] && . $HOME/vdrplayer.conf
+
+function _vdr() {
+ # Max waiting time in Seconds
+ n=10
+ # Check if VDR is running
+ if [ `ps -C vdr | grep -cw "vdr"` -eq 0 ]; then
+ # test if vdr is running
+ while [ `ps -C vdr | grep -cw "vdr"` -eq 0 ] && [ $n -gt 0 ]; do
+ sleep 1
+ (( n=$n-1 ))
+ [ $n -eq 0 ] && return 1
+ done
+ fi
+ return 0
+}
+
+function _vdrip() {
+ VDRIP=`$DIALOG --inputbox "VDR Server IP" 10 50 "127.0.0.1" 3>&1 1>&2 2>&3`
+ [ $? != 0 ] && return 1
+ [ "$VDRIP" = "" ] && return 1
+ echo "VDRIP=$VDRIP" >> $HOME/vdrplayer.conf
+ return 0
+}
+
+function _shmclient() {
+ CMD="--fullscreen"
+ ShmClient $CMD
+ return 0
+}
+
+function _tvtime() {
+# chmod 666 /dev/rtc
+# echo 1024 > /proc/sys/dev/rtc/max-user-freq
+ CMD="--fullscreen --display=$DISPLAY"
+ tvtime $CMD
+ sleep 2
+ tvtime-command MENU_EXIT
+ return 0
+}
+
+function _vdrtvtime() {
+# chmod 666 /dev/rtc
+# echo 1024 > /proc/sys/dev/rtc/max-user-freq
+ svdrpsend.pl PLUG vdrtvtime start
+ return 0
+}
+
+function _vdrsxfe() {
+ [ -n "$VDRIP" ] || _vdrip || return 1
+ CMD="--video=xv --fullscreen --post tvtime:method=LinearBlend,cheap_mode=1,pulldown=0,use_progressive_frame_flag=1"
+ MRL="xvdr+tcp://$VDRIP"
+ vdr-sxfe $CMD $MRL
+ return 0
+}
+
+function _xineliboutput() {
+ [ -n "$VDRIP" ] || _vdrip || return 1
+ CMD="--fullscreen --hide-gui"
+ MRL="xvdr+tcp://$VDRIP#nocache;demux:mpeg_block"
+ xine $CMD "$MRL"
+ return 0
+}
+
+function _xine() {
+ CMD="--fullscreen --hide-gui --post vdr --post vdr_audio"
+ MRL="vdr:/tmp/vdr-xine/stream#demux:mpeg_pes"
+ xine $CMD "$MRL"
+ return 0
+}
+
+function _xinenetwork() {
+ [ -n "$VDRIP" ] || _vdrip || return 1
+ CMD="--fullscreen --hide-gui --post vdr --post vdr_audio"
+ MRL="netvdr://$VDRIP#demux:mpeg_pes"
+ xine $CMD "$MRL"
+ return 0
+}
+
+### Main Script ###############################################################################
+
+_vdr || exit 1 # Check if VDR is running
+
+[ -n "`ps x | grep ShmClient`" ] && killall -q -9 ShmClient # If ShmClient is running, stop it
+[ -n "`ps x | grep tvtime`" ] && killall -q -9 tvtime # If tvtime is running, stop it
+[ -n "`ps x | grep vdr-sxfe`" ] && killall -q -9 vdr-sxfe # If vdr-sxfe is running, stop it
+[ -n "`ps cx | grep xine`" ] && killall -q -9 xine # If xine is running, stop it
+[ "$VDRPLAYER" = "vdrtvtime" ] && svdrpsend.pl PLUG vdrtvtime stop
+
+# Player
+if [ -n "$VDRPLAYER" ]; then
+ case $VDRPLAYER in
+ shmclient) _shmclient ;;
+ tvtime) _tvtime ;;
+ vdrtvtime) _vdrtvtime ;;
+ vdr-sxfe) _vdrsxfe ;;
+ xineliboutput) _xineliboutput ;;
+ xine) _xine ;;
+ xine-network) _xinenetwork ;;
+ *) VDRPLAYER="" ;;
+ esac
+fi
+
+# Dialog
+if [ -z "$VDRPLAYER" ]; then
+ option=`$DIALOG --menu \
+ 'Aktiviere den Player, den du nutzen moechtest.' 0 0 8 \
+ '1' 'ShmClient (Softdevice)' \
+ '2' 'Tvtime (FF-Karte)' \
+ '3' 'vdrtvtime (tvtime als Plugin, FF-Karte)' \
+ '4' 'vdr-sxfe (Xineliboutput)' \
+ '5' 'Xine' \
+ '6' 'Xine (Xineliboutput)' \
+ '7' 'Xine-Network' 3>&1 1>&2 2>&3`
+ [ $? != 0 ] && exit 1
+ VDRIP=
+ case $option in
+ 1) echo "VDRPLAYER=\"shmclient\"" > $HOME/vdrplayer.conf && _shmclient ;;
+ 2) echo "VDRPLAYER=\"tvtime\"" > $HOME/vdrplayer.conf && _tvtime ;;
+ 3) echo "VDRPLAYER=\"vdrtvtime\"" > $HOME/vdrplayer.conf && _vdrtvtime ;;
+ 4) echo "VDRPLAYER=\"vdr-sxfe\"" > $HOME/vdrplayer.conf && _vdrsxfe ;;
+ 5) echo "VDRPLAYER=\"xine\"" > $HOME/vdrplayer.conf && _xine ;;
+ 6) echo "VDRPLAYER=\"xineliboutput\"" > $HOME/vdrplayer.conf && _xineliboutput ;;
+ 7) echo "VDRPLAYER=\"xine-network\"" > $HOME/vdrplayer.conf && _xinenetwork ;;
+ esac
+fi
diff --git a/vdr/conf/channels.conf b/vdr/conf/channels.conf
new file mode 100644
index 0000000..6566ab0
--- /dev/null
+++ b/vdr/conf/channels.conf
@@ -0,0 +1,128 @@
+:Main TV
+Das Erste;ARD:11836:hC34:S19.2E:27500:101:102=deu;106=deu:104:0:28106:1:1101:0
+ZDF;ZDFvision:11953:hC34:S19.2E:27500:110:120=deu,121=2ch;125=dd:130:0:28006:1:1079:0
+WDR Köln;ARD:11836:hC34:S19.2E:27500:601:602=deu:604:0:28111:1:1101:0
+SAT.1;ProSiebenSat.1:12480:vC34:S19.2E:27500:1791:1792=deu;1795=deu:34:0:46:133:33:0
+KABEL1;ProSiebenSat.1:12480:vC34:S19.2E:27500:511:512=deu:33:0:899:133:33:0
+ProSieben;ProSiebenSat.1:12480:vC34:S19.2E:27500:255:256=deu;257=deu:32:0:898:133:33:0
+RTL Television,RTL;RTL World:12187:hC34:S19.2E:27500:163:104=deu;106=deu:105:0:12003:1:1089:0
+RTL2;RTL World:12187:hC34:S19.2E:27500:166:128=deu:68:0:12020:1:1089:0
+Super RTL,S RTL;RTL World:12187:hC34:S19.2E:27500:165:120=deu:65:0:12040:1:1089:0
+VOX;RTL World:12187:hC34:S19.2E:27500:167:136=deu:71:0:12060:1:1089:0
+Bayerisches FS;ARD:11836:hC34:S19.2E:27500:201:202=deu:204:0:28107:1:1101:0
+hr-fernsehen;ARD:11836:hC34:S19.2E:27500:301:302=deu:304:0:28108:1:1101:0
+NDR FS NDS;ARD:12109:hC34:S19.2E:27500:2401:2402=deu:2404:0:28226:1:1073:0
+MDR FERNSEHEN;ARD:12109:hC34:S19.2E:27500:401:402=deu:404:0:28204:1:1073:0
+rbb Berlin;ARD:12109:hC34:S19.2E:27500:601:602=deu:604:0:28206:1:1073:0
+SR SÜDWEST Ferns.;ARD:12265:hC34:S19.2E:27500:1301:1302=deu:1304:0:28486:1:1093:0
+BR-alpha;ARD:11836:hC34:S19.2E:27500:701:702=deu:704:0:28112:1:1101:0
+Phoenix;ARD:11836:hC34:S19.2E:27500:901:902=deu:904:0:28114:1:1101:0
+arte;ARD:11836:hC34:S19.2E:27500:401:402=deu,403=fra:404:0:28109:1:1101:0
+3sat;ZDFvision:11953:hC34:S19.2E:27500:210:220=deu,221=2ch;225=dd:230:0:28007:1:1079:0
+KiKa;ZDFvision:11953:hC34:S19.2E:27500:310:320=deu:330:0:28008:1:1079:0
+ZDFdokukanal;ZDFvision:11953:hC34:S19.2E:27500:660:670=deu:630:0:28014:1:1079:0
+ZDFinfokanal;ZDFvision:11953:hC34:S19.2E:27500:610:620=deu:630:0:28011:1:1079:0
+ZDFtheaterkanal;ZDFvision:11953:hC34:S19.2E:27500:1110:1120=deu:630:0:28016:1:1079:0
+EinsPlus;ARD:12109:hC34:S19.2E:27500:301:302=deu:0:0:28203:1:1073:0
+EinsExtra;ARD:12109:hC34:S19.2E:27500:101:102=deu:0:0:28201:1:1073:0
+EinsFestival;ARD:12109:hC34:S19.2E:27500:201:202=deu:204:0:28202:1:1073:0
+:More TV
+DSF;BetaDigital:12480:vC34:S19.2E:27500:1023:1024=deu:39:0:900:133:33:0
+Eurosport;SES Astra:12226:hC34:S19.2E:27500:101+8190:103=deu:102:0:31200:1:1091:0
+n-tv;RTL World:12187:hC34:S19.2E:27500:169:73=deu:80:0:12090:1:1089:0
+N24;ProSiebenSat.1:12480:vC34:S19.2E:27500:2047:2048=deu:36:0:47:133:33:0
+EuroNews;GlobeCast:12226:hC34:S19.2E:27500:2432:2433=deu,2434=eng,2435=fra,2436=ita,2437=esl,2438=por,2439=rus,2440:0:0:31220:1:1091:0
+9Live;BetaDigital:12480:vC34:S19.2E:27500:767:768=deu:35:0:897:133:33:0
+XXP;T-Systems:12633:hC56:S19.2E:22000:203:303=deu:503:0:12602:1:1113:0
+:Music
+MTV Germany;MTV Networks:11973:V:S19.2E:27500:4031:4032:4034:0:28673:1:1078:0
+NICK;MTV Networks:11973:vC34:S19.2E:27500:4101+8190:4102=deu:4104:0:28680:1:1078:0
+VIVA;MTV Networks:11973:vC34:S19.2E:27500:4061+8190:4062:4064:0:28676:1:1078:0
+VIVA PLUS;MTV Networks:11973:vC34:S19.2E:27500:4071+8190:4072:4074:0:28677:1:1078:0
+:@100 Radio
+Antenne Brandenburg;ARD rbb:12265:hC34:S19.2E:27500:0:821=deu:0:0:28454:1:1093:0
+Bayern 1;ARD BR:12265:hC34:S19.2E:27500:0:101=deu:0:0:28400:1:1093:0
+Bayern2Radio;ARD BR:12265:hC34:S19.2E:27500:0:111=deu:0:0:28401:1:1093:0
+Bayern 3;ARD BR:12265:hC34:S19.2E:27500:0:121=deu:0:0:28402:1:1093:0
+Bayern 4 Klassik;ARD BR:12265:hC34:S19.2E:27500:0:131=deu;132=deu:0:0:28403:1:1093:0
+B5 aktuell;ARD BR:12265:hC34:S19.2E:27500:0:141=deu:0:0:28404:1:1093:0
+BAYERN MOBIL;ARD BR:12265:hC34:S19.2E:27500:0:151=deu:0:0:28405:1:1093:0
+BR Verkehrskanal;ARD BR:12265:hC34:S19.2E:27500:0:171=deu:0:0:28407:1:1093:0
+Bremen Eins;ARD RB:12265:hC34:S19.2E:27500:0:701=deu:0:0:28448:1:1093:0
+Bremen Vier;ARD RB:12265:hC34:S19.2E:27500:0:721=deu:0:0:28450:1:1093:0
+cont.ra;ARD SWR:12265:hC34:S19.2E:27500:0:1071=deu:0:0:28472:1:1093:0
+DASDING;ARD SWR:12265:hC34:S19.2E:27500:0:1061=deu:0:0:28471:1:1093:0
+DAS MODUL;ARD BR:12265:hC34:S19.2E:27500:0:161=deu:0:0:28406:1:1093:0
+DLF;ZDFvision:11953:hC34:S19.2E:27500:0:810=deu:0:0:28013:1:1079:0
+DKULTUR;ZDFvision:11953:hC34:S19.2E:27500:0:710=deu:0:0:28012:1:1079:0
+DW 1;ARD DW:12265:hC34:S19.2E:27500:0:301:0:0:28416:1:1093:0
+DW 4;ARD DW:12265:hC34:S19.2E:27500:0:311:0:0:28417:1:1093:0
+Eins Live;ARD WDR:12265:hC34:S19.2E:27500:0:1101=deu:0:0:28475:1:1093:0
+Eins Live diggi;ARD WDR:12265:hC34:S19.2E:27500:0:1161=deu:0:0:28481:1:1093:0
+Fritz;ARD rbb:12265:hC34:S19.2E:27500:0:851=deu:0:0:28457:1:1093:0
+hr1;ARD HR:12265:hC34:S19.2E:27500:0:401=deu:0:0:28419:1:1093:0
+hr2;ARD HR:12265:hC34:S19.2E:27500:0:411=deu;412=deu:0:0:28420:1:1093:0
+hr3;ARD HR:12265:hC34:S19.2E:27500:0:421=deu:0:0:28421:1:1093:0
+hr4;ARD HR:12265:hC34:S19.2E:27500:0:431=deu:0:0:28422:1:1093:0
+hr-info;ARD HR:12265:hC34:S19.2E:27500:0:461=deu:0:0:28424:1:1093:0
+hr-info +;ARD HR:12265:hC34:S19.2E:27500:0:471=deu:0:0:28425:1:1093:0
+InfoRadio;ARD rbb:12265:hC34:S19.2E:27500:0:801=deu:0:0:28452:1:1093:0
+Inselradio;T-Systems:12633:hC56:S19.2E:22000:0:702:0:0:12651:1:1113:0
+kulturradio;ARD rbb:12265:hC34:S19.2E:27500:0:811=deu:0:0:28453:1:1093:0
+MDR1 SACHSEN;ARD MDR:12265:hC34:S19.2E:27500:0:501=deu:0:0:28428:1:1093:0
+MDR1 S-ANHALT;ARD MDR:12265:hC34:S19.2E:27500:0:511=deu:0:0:28429:1:1093:0
+MDR1 THÜRINGEN;ARD MDR:12265:hC34:S19.2E:27500:0:521=deu:0:0:28430:1:1093:0
+MDR FIGARO;ARD MDR:12265:hC34:S19.2E:27500:0:531=deu:0:0:28431:1:1093:0
+MDR JUMP;ARD MDR:12265:hC34:S19.2E:27500:0:541=deu:0:0:28432:1:1093:0
+MDR INFO;ARD MDR:12265:hC34:S19.2E:27500:0:561=deu:0:0:28434:1:1093:0
+NDR 2;ARD NDR:12265:hC34:S19.2E:27500:0:601=deu:0:0:28437:1:1093:0
+NDR Kultur;ARD NDR:12265:hC34:S19.2E:27500:0:611=deu:0:0:28438:1:1093:0
+NDR Info;ARD NDR:12265:hC34:S19.2E:27500:0:621=deu:0:0:28439:1:1093:0
+NDR 90,3;ARD NDR:12265:hC34:S19.2E:27500:0:641=deu:0:0:28441:1:1093:0
+NDR 1 Welle Nord;ARD NDR:12265:hC34:S19.2E:27500:0:651=deu:0:0:28442:1:1093:0
+NDR 1 Radio MV;ARD NDR:12265:hC34:S19.2E:27500:0:661=deu:0:0:28443:1:1093:0
+NDR 1 Niedersachsen;ARD NDR:12265:hC34:S19.2E:27500:0:671=deu:0:0:28444:1:1093:0
+NDR Info Spezial;ARD NDR:12265:hC34:S19.2E:27500:0:681=deu:0:0:28445:1:1093:0
+Nordwestradio;ARD RB:12265:hC34:S19.2E:27500:0:711=deu:0:0:28449:1:1093:0
+N-JOY;ARD NDR:12265:hC34:S19.2E:27500:0:631=deu:0:0:28440:1:1093:0
+radioBERLIN 88,8;ARD rbb:12265:hC34:S19.2E:27500:0:831=deu:0:0:28455:1:1093:0
+radioeins;ARD rbb:12265:hC34:S19.2E:27500:0:841=deu:0:0:28456:1:1093:0
+radiomultikulti;ARD rbb:12265:hC34:S19.2E:27500:0:861=deu:0:0:28458:1:1093:0
+radio top40;T-Systems:12633:hC56:S19.2E:22000:0:353=deu:0:0:12653:1:1113:0
+SPUTNIK;ARD MDR:12265:hC34:S19.2E:27500:0:551=deu:0:0:28433:1:1093:0
+SR1;ARD SR:12265:hC34:S19.2E:27500:0:901=deu:0:0:28461:1:1093:0
+SR2;ARD SR:12265:hC34:S19.2E:27500:0:911=deu:0:0:28462:1:1093:0
+SR3;ARD SR:12265:hC34:S19.2E:27500:0:921=deu:0:0:28463:1:1093:0
+SWR1 BW;ARD SWR:12265:hC34:S19.2E:27500:0:1001=deu:0:0:28465:1:1093:0
+SWR1 RP;ARD SWR:12265:hC34:S19.2E:27500:0:1011=deu:0:0:28466:1:1093:0
+SWR2;ARD SWR:12265:hC34:S19.2E:27500:0:1021=deu;1022=deu:0:0:28467:1:1093:0
+SWR3;ARD SWR:12265:hC34:S19.2E:27500:0:1031=deu:0:0:28468:1:1093:0
+SWR4 BW;ARD SWR:12265:hC34:S19.2E:27500:0:1041=deu:0:0:28469:1:1093:0
+SWR4 RP;ARD SWR:12265:hC34:S19.2E:27500:0:1051=deu:0:0:28470:1:1093:0
+WDR 2;ARD WDR:12265:hC34:S19.2E:27500:0:1111=deu:0:0:28476:1:1093:0
+WDR 3;ARD WDR:12265:hC34:S19.2E:27500:0:1121=deu:0:0:28477:1:1093:0
+WDR 4;ARD WDR:12265:hC34:S19.2E:27500:0:1131=deu:0:0:28478:1:1093:0
+WDR 5;ARD WDR:12265:hC34:S19.2E:27500:0:1141=deu;1142=deu:0:0:28479:1:1093:0
+WDR 2 Klassik;ARD WDR:12265:hC34:S19.2E:27500:0:1171=deu:0:0:28482:1:1093:0
+WDR Event;ARD WDR:12265:hC34:S19.2E:27500:0:1181=deu:0:0:28483:1:1093:0
+WDR Funkhaus Europa;ARD WDR:12265:hC34:S19.2E:27500:0:1151=deu:0:0:28480:1:1093:0
+YOU FM;ARD HR:12265:hC34:S19.2E:27500:0:451=deu:0:0:28423:1:1093:0
+:@200 die dritten
+MDR S-ANHALT;ARD:12109:hC34:S19.2E:27500:401:402=deu:404:0:28229:1:1073:0
+MDR SACHSEN;ARD:12109:hC34:S19.2E:27500:401:402=deu:404:0:28228:1:1073:0
+MDR THÜRINGEN;ARD:12109:hC34:S19.2E:27500:401:402=deu:404:0:28230:1:1073:0
+NDR FS HH;ARD:12109:hC34:S19.2E:27500:2401:2402=deu:2404:0:28225:1:1073:0
+NDR FS SH;ARD:12109:hC34:S19.2E:27500:2401:2402=deu:2404:0:28227:1:1073:0
+NDR FS MV;ARD:12109:hC34:S19.2E:27500:2401:2402=deu:2404:0:28224:1:1073:0
+rbb Brandenburg;ARD:12109:hC34:S19.2E:27500:601:602=deu:604:0:28205:1:1073:0
+SÜDWEST Ferns. BW;ARD:11836:hC34:S19.2E:27500:801:802=deu:804:0:28113:1:1101:0
+SÜDWEST Ferns. RP;ARD:12109:hC34:S19.2E:27500:3101:3102=deu:3104:0:28231:1:1073:0
+WDR Aachen;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28305:1:1201:0
+WDR Bielefeld;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28306:1:1201:0
+WDR Dortmund;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28307:1:1201:0
+WDR Düsseldorf;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28308:1:1201:0
+WDR Essen;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28309:1:1201:0
+WDR Münster;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28310:1:1201:0
+WDR Siegen;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28311:1:1201:0
+WDR Wuppertal;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28312:1:1201:0
+:@1000 New channels
diff --git a/vdr/conf/channels.conf.dvb-c_unity-media b/vdr/conf/channels.conf.dvb-c_unity-media
new file mode 100644
index 0000000..709a925
--- /dev/null
+++ b/vdr/conf/channels.conf.dvb-c_unity-media
@@ -0,0 +1,168 @@
+:FreeTV
+Das Erste;ARD:410000:C0M256:C:6900:101=2:102=deu,103=2ch;106=dd:104:0:28106:41985:1101:0
+ZDF;ZDFvision:394000:C0M256:C:6900:110=2:120=deu,121=2ch;125=deu:130:0:28006:1:1079:0
+WDR Köln;ARD:410000:C0M256:C:6900:601=2:602=deu,603=2ch:604:0:28111:41985:1101:0
+3sat;ZDFvision:394000:C0M256:C:6900:210=2:220=deu,221=2ch;225=dd:230:0:28007:1:1079:0
+KiKa;ZDFvision:394000:C0M256:C:6900:310=2:320=deu:330:0:28008:1:1079:0
+ZDFinfokanal;ZDFvision:394000:C0M256:C:6900:610=2:620=deu:630:0:28011:1:1079:0
+ZDFdokukanal;ZDFvision:394000:C0M256:C:6900:660=2:670=deu,671=2ch:630:0:28014:1:1079:0
+ZDFtheaterkanal;ZDFvision:394000:C0M256:C:6900:1110=2:1120=deu,1121=2ch:630:0:28016:1:1079:0
+EinsExtra;ARD:682000:C0M256:C:6900:101=2:102=deu:0:0:28721:41985:1051:0
+EinsFestival;ARD:682000:C0M256:C:6900:201=2:202=deu:204:0:28722:41985:1051:0
+EinsPlus;ARD:682000:C0M256:C:6900:301=2:302=deu:304:0:28723:41985:1051:0
+arte;ARD:682000:C0M256:C:6900:401=2:402=deu,403=fra:404:0:28724:41985:1051:0
+Phoenix;ARD:682000:C0M256:C:6900:501=2:502=deu:504:0:28725:41985:1051:0
+BR-alpha;ARD:682000:C0M256:C:6900:1401=2:1402=deu;1406=deu:1404:0:28487:41985:1051:0
+Bayerisches FS Süd;ARD:410000:C0M256:C:6900:201=2:202=deu,203=2ch;206=dd:204:0:28107:41985:1101:0
+Bayerisches FS Nord;ARD:410000:C0M256:C:6900:201=2:202=deu,203=deu;206=deu:204:0:28110:41985:1101:0
+hr-fernsehen;ARD:410000:C0M256:C:6900:301=2:302=deu,303=2ch:304:0:28108:41985:1101:0
+SWR Fernsehen BW;ARD:410000:C0M256:C:6900:801=2:802=deu:804:0:28113:41985:1101:0
+SWR Fernsehen RP;ARD:426000:C0M256:C:6900:3101=2:3102=deu:3104:0:28231:41985:1073:0
+rbb Brandenburg;ARD:426000:C0M256:C:6900:601=2:602=deu:604:0:28205:41985:1073:0
+rbb Berlin;ARD:426000:C0M256:C:6900:601=2:602=deu:604:0:28206:41985:1073:0
+NDR FS MV;ARD:426000:C0M256:C:6900:2601=2:2602=deu,2603=2ch:2604:0:28224:41985:1073:0
+NDR FS HH;ARD:426000:C0M256:C:6900:2601=2:2602=deu,2603=2ch:2604:0:28225:41985:1073:0
+NDR FS NDS;ARD:426000:C0M256:C:6900:2601=2:2602=deu,2603=2ch:2604:0:28226:41985:1073:0
+NDR FS SH;ARD:426000:C0M256:C:6900:2601=2:2602=deu,2603=2ch:2604:0:28227:41985:1073:0
+MDR Sachsen;ARD:426000:C0M256:C:6900:2901=2:2902=deu,2903=2ch:2904:0:28228:41985:1073:0
+MDR S-Anhalt;ARD:426000:C0M256:C:6900:2901=2:2902=deu,2903=2ch:2904:0:28229:41985:1073:0
+MDR Thüringen;ARD:426000:C0M256:C:6900:2901=2:2902=deu,2903=2ch:2904:0:28230:41985:1073:0
+EinsFestival HD;ARD:682000:C0M256:C:6900:1601=27:1602=deu;1606=deu:0:0:28396:41985:1051:0
+:Radio
+Bayern 1;ARD BR:410000:C0M256:C:6900:0=2:3101=deu:0:0:28400:41985:1101:0
+Bayern 2;ARD BR:410000:C0M256:C:6900:0=2:3111=deu:0:0:28401:41985:1101:0
+BAYERN 3;ARD BR:410000:C0M256:C:6900:0=2:3121=deu:0:0:28402:41985:1101:0
+BAYERN 4 KLASSIK;ARD BR:410000:C0M256:C:6900:0=2:3131=deu;3132=deu:0:0:28403:41985:1101:0
+B5 aktuell;ARD BR:410000:C0M256:C:6900:0=2:3141=deu:0:0:28404:41985:1101:0
+BAYERN plus;ARD BR:410000:C0M256:C:6900:0=2:3151=deu:0:0:28405:41985:1101:0
+on3radio;ARD BR:410000:C0M256:C:6900:0=2:3161=deu:0:0:28406:41985:1101:0
+BR Verkehr;ARD BR:410000:C0M256:C:6900:0=2:3171=deu:0:0:28407:41985:1101:0
+B5 plus;ARD BR:410000:C0M256:C:6900:0=2:3181=deu:0:0:28408:41985:1101:0
+hr1;ARD HR:410000:C0M256:C:6900:0=2:3401=deu:0:0:28419:41985:1101:0
+hr2;ARD HR:410000:C0M256:C:6900:0=2:3411=deu;3412=deu:0:0:28420:41985:1101:0
+hr3;ARD HR:410000:C0M256:C:6900:0=2:3421=deu:0:0:28421:41985:1101:0
+hr4;ARD HR:410000:C0M256:C:6900:0=2:3431=deu:0:0:28422:41985:1101:0
+YOU FM;ARD HR:410000:C0M256:C:6900:0=2:3451=deu:0:0:28423:41985:1101:0
+hr iNFO;ARD HR:410000:C0M256:C:6900:0=2:3461=deu:0:0:28424:41985:1101:0
+hr iNFO +;ARD HR:410000:C0M256:C:6900:0=2:3471=deu:0:0:28425:41985:1101:0
+SR1 Europawelle;ARD SR:410000:C0M256:C:6900:0=2:3901=deu:0:0:28461:41985:1101:0
+SR2 KulturRadio;ARD SR:410000:C0M256:C:6900:0=2:3911=deu:0:0:28462:41985:1101:0
+SR3 Saarlandwelle;ARD SR:410000:C0M256:C:6900:0=2:3921=deu:0:0:28463:41985:1101:0
+1LIVE;ARD WDR:410000:C0M256:C:6900:0=2:4101=deu:0:0:28475:41985:1101:0
+WDR 2;ARD WDR:410000:C0M256:C:6900:0=2:4111=deu:0:0:28476:41985:1101:0
+WDR 3;ARD WDR:410000:C0M256:C:6900:0=2:4121=deu;4122=deu:0:0:28477:41985:1101:0
+WDR 4;ARD WDR:410000:C0M256:C:6900:0=2:4131=deu:0:0:28478:41985:1101:0
+WDR 5;ARD WDR:410000:C0M256:C:6900:0=2:4141=deu:0:0:28479:41985:1101:0
+WDR Funkhaus Europa;ARD WDR:410000:C0M256:C:6900:0=2:4151=deu:0:0:28480:41985:1101:0
+1LIVE diggi;ARD WDR:410000:C0M256:C:6900:0=2:4161=deu:0:0:28481:41985:1101:0
+WDR 2 Klassik;ARD WDR:410000:C0M256:C:6900:0=2:4171=deu:0:0:28482:41985:1101:0
+WDR Event;ARD WDR:410000:C0M256:C:6900:0=2:4181=deu:0:0:28483:41985:1101:0
+DKULTUR;ZDFvision:394000:C0M256:C:6900:0=2:710=deu:0:0:28012:1:1079:0
+DLF;ZDFvision:394000:C0M256:C:6900:0=2:810=deu:0:0:28013:1:1079:0
+Dok&Deb;ZDFvision:394000:C0M256:C:6900:0=2:410=deu:0:0:28017:1:1079:0
+DW 1;ARD DW:426000:C0M256:C:6900:0:3301=deu:0:0:28416:41985:1073:0
+DW 4;ARD DW:426000:C0M256:C:6900:0:3311=deu:0:0:28417:41985:1073:0
+MDR1 SACHSEN;ARD MDR:426000:C0M256:C:6900:0:3501=deu:0:0:28428:41985:1073:0
+MDR1 SA-ANHALT;ARD MDR:426000:C0M256:C:6900:0:3511=deu:0:0:28429:41985:1073:0
+MDR1 THÜRINGEN;ARD MDR:426000:C0M256:C:6900:0:3521=deu:0:0:28430:41985:1073:0
+MDR FIGARO;ARD MDR:426000:C0M256:C:6900:0:3531=deu:0:0:28431:41985:1073:0
+JUMP;ARD MDR:426000:C0M256:C:6900:0:3541=deu:0:0:28432:41985:1073:0
+MDR SPUTNIK;ARD MDR:426000:C0M256:C:6900:0:3551=deu:0:0:28433:41985:1073:0
+MDR Info;ARD MDR:426000:C0M256:C:6900:0:3561=deu:0:0:28434:41985:1073:0
+MDR KLASSIK;ARD MDR:426000:C0M256:C:6900:0:3571=deu:0:0:28435:41985:1073:0
+NDR 2;ARD NDR:426000:C0M256:C:6900:0:3601=deu:0:0:28437:41985:1073:0
+NDR Kultur;ARD NDR:426000:C0M256:C:6900:0:3611=deu:0:0:28438:41985:1073:0
+NDR Info;ARD NDR:426000:C0M256:C:6900:0:3621=deu:0:0:28439:41985:1073:0
+N-JOY;ARD NDR:426000:C0M256:C:6900:0:3631=deu:0:0:28440:41985:1073:0
+NDR 90,3;ARD NDR:426000:C0M256:C:6900:0:3641=deu:0:0:28441:41985:1073:0
+NDR1WelleNord;ARD NDR:426000:C0M256:C:6900:0:3651=deu:0:0:28442:41985:1073:0
+NDR 1 Radio MV;ARD NDR:426000:C0M256:C:6900:0:3661=deu:0:0:28443:41985:1073:0
+NDR 1 Nieders.;ARD NDR:426000:C0M256:C:6900:0:3671=deu:0:0:28444:41985:1073:0
+NDR Info Spez.;ARD NDR:426000:C0M256:C:6900:0:3681=deu:0:0:28445:41985:1073:0
+Bremen Eins;ARD RB:426000:C0M256:C:6900:0:3701=deu:0:0:28448:41985:1073:0
+Nordwestradio;ARD RB:426000:C0M256:C:6900:0:3711=deu:0:0:28449:41985:1073:0
+Bremen Vier;ARD RB:426000:C0M256:C:6900:0:3721=deu:0:0:28450:41985:1073:0
+Inforadio;ARD rbb:426000:C0M256:C:6900:0:3801=deu:0:0:28452:41985:1073:0
+Kulturradio;ARD rbb:426000:C0M256:C:6900:0:3811=deu:0:0:28453:41985:1073:0
+Antenne Brandenburg;ARD rbb:426000:C0M256:C:6900:0:3821=deu:0:0:28454:41985:1073:0
+radioBERLIN 88,8;ARD rbb:426000:C0M256:C:6900:0:3831=deu:0:0:28455:41985:1073:0
+radioeins;ARD rbb:426000:C0M256:C:6900:0:3841=deu:0:0:28456:41985:1073:0
+Fritz;ARD rbb:426000:C0M256:C:6900:0:3851=deu:0:0:28457:41985:1073:0
+SWR 1 BW;ARD SWR:426000:C0M256:C:6900:0:4001=deu:0:0:28465:41985:1073:0
+SWR 1 RP;ARD SWR:426000:C0M256:C:6900:0:4011=deu:0:0:28466:41985:1073:0
+SWR 2;ARD SWR:426000:C0M256:C:6900:0:4021=deu;4022=deu:0:0:28467:41985:1073:0
+SWR 3;ARD SWR:426000:C0M256:C:6900:0:4031=deu:0:0:28468:41985:1073:0
+SWR 4 BW;ARD SWR:426000:C0M256:C:6900:0:4041=deu:0:0:28469:41985:1073:0
+SWR 4 RP;ARD SWR:426000:C0M256:C:6900:0:4051=deu:0:0:28470:41985:1073:0
+DASDING;ARD SWR:426000:C0M256:C:6900:0:4061=deu:0:0:28471:41985:1073:0
+SWR cont.ra;ARD SWR:426000:C0M256:C:6900:0:4071=deu:0:0:28472:41985:1073:0
+:Radio
+Bayern 1;ARD BR:410000:C0M256:C:6900:0:3101=deu:0:0:28400:41985:1101:0
+Bayern 2;ARD BR:410000:C0M256:C:6900:0:3111=deu:0:0:28401:41985:1101:0
+BAYERN 3;ARD BR:410000:C0M256:C:6900:0:3121=deu:0:0:28402:41985:1101:0
+BAYERN 4 KLASSIK;ARD BR:410000:C0M256:C:6900:0:3131=deu;3132=deu:0:0:28403:41985:1101:0
+B5 aktuell;ARD BR:410000:C0M256:C:6900:0:3141=deu:0:0:28404:41985:1101:0
+BAYERN plus;ARD BR:410000:C0M256:C:6900:0:3151=deu:0:0:28405:41985:1101:0
+on3radio;ARD BR:410000:C0M256:C:6900:0:3161=deu:0:0:28406:41985:1101:0
+BR Verkehr;ARD BR:410000:C0M256:C:6900:0:3171=deu:0:0:28407:41985:1101:0
+B5 plus;ARD BR:410000:C0M256:C:6900:0:3181=deu:0:0:28408:41985:1101:0
+hr1;ARD HR:410000:C0M256:C:6900:0:3401=deu:0:0:28419:41985:1101:0
+hr2;ARD HR:410000:C0M256:C:6900:0:3411=deu;3412=deu:0:0:28420:41985:1101:0
+hr3;ARD HR:410000:C0M256:C:6900:0:3421=deu:0:0:28421:41985:1101:0
+hr4;ARD HR:410000:C0M256:C:6900:0:3431=deu:0:0:28422:41985:1101:0
+YOU FM;ARD HR:410000:C0M256:C:6900:0:3451=deu:0:0:28423:41985:1101:0
+hr iNFO;ARD HR:410000:C0M256:C:6900:0:3461=deu:0:0:28424:41985:1101:0
+hr iNFO +;ARD HR:410000:C0M256:C:6900:0:3471=deu:0:0:28425:41985:1101:0
+SR1 Europawelle;ARD SR:410000:C0M256:C:6900:0:3901=deu:0:0:28461:41985:1101:0
+SR2 KulturRadio;ARD SR:410000:C0M256:C:6900:0:3911=deu:0:0:28462:41985:1101:0
+SR3 Saarlandwelle;ARD SR:410000:C0M256:C:6900:0:3921=deu:0:0:28463:41985:1101:0
+1LIVE;ARD WDR:410000:C0M256:C:6900:0:4101=deu:0:0:28475:41985:1101:0
+WDR 2;ARD WDR:410000:C0M256:C:6900:0:4111=deu:0:0:28476:41985:1101:0
+WDR 3;ARD WDR:410000:C0M256:C:6900:0:4121=deu;4122=deu:0:0:28477:41985:1101:0
+WDR 4;ARD WDR:410000:C0M256:C:6900:0:4131=deu:0:0:28478:41985:1101:0
+WDR 5;ARD WDR:410000:C0M256:C:6900:0:4141=deu:0:0:28479:41985:1101:0
+WDR Funkhaus Europa;ARD WDR:410000:C0M256:C:6900:0:4151=deu:0:0:28480:41985:1101:0
+1LIVE diggi;ARD WDR:410000:C0M256:C:6900:0:4161=deu:0:0:28481:41985:1101:0
+WDR 2 Klassik;ARD WDR:410000:C0M256:C:6900:0:4171=deu:0:0:28482:41985:1101:0
+WDR Event;ARD WDR:410000:C0M256:C:6900:0:4181=deu:0:0:28483:41985:1101:0
+DKULTUR;ZDFvision:394000:C0M256:C:6900:0=2:710=deu:0:0:28012:1:1079:0
+DLF;ZDFvision:394000:C0M256:C:6900:0=2:810=deu:0:0:28013:1:1079:0
+Dok&Deb;ZDFvision:394000:C0M256:C:6900:0=2:410=deu:0:0:28017:1:1079:0
+DW 1;ARD DW:426000:C0M256:C:6900:0:3301=deu:0:0:28416:41985:1073:0
+DW 4;ARD DW:426000:C0M256:C:6900:0:3311=deu:0:0:28417:41985:1073:0
+MDR1 SACHSEN;ARD MDR:426000:C0M256:C:6900:0:3501=deu:0:0:28428:41985:1073:0
+MDR1 SA-ANHALT;ARD MDR:426000:C0M256:C:6900:0:3511=deu:0:0:28429:41985:1073:0
+MDR1 THÜRINGEN;ARD MDR:426000:C0M256:C:6900:0:3521=deu:0:0:28430:41985:1073:0
+MDR FIGARO;ARD MDR:426000:C0M256:C:6900:0:3531=deu:0:0:28431:41985:1073:0
+JUMP;ARD MDR:426000:C0M256:C:6900:0:3541=deu:0:0:28432:41985:1073:0
+MDR SPUTNIK;ARD MDR:426000:C0M256:C:6900:0:3551=deu:0:0:28433:41985:1073:0
+MDR Info;ARD MDR:426000:C0M256:C:6900:0:3561=deu:0:0:28434:41985:1073:0
+MDR KLASSIK;ARD MDR:426000:C0M256:C:6900:0:3571=deu:0:0:28435:41985:1073:0
+NDR 2;ARD NDR:426000:C0M256:C:6900:0:3601=deu:0:0:28437:41985:1073:0
+NDR Kultur;ARD NDR:426000:C0M256:C:6900:0:3611=deu:0:0:28438:41985:1073:0
+NDR Info;ARD NDR:426000:C0M256:C:6900:0:3621=deu:0:0:28439:41985:1073:0
+N-JOY;ARD NDR:426000:C0M256:C:6900:0:3631=deu:0:0:28440:41985:1073:0
+NDR 90,3;ARD NDR:426000:C0M256:C:6900:0:3641=deu:0:0:28441:41985:1073:0
+NDR1WelleNord;ARD NDR:426000:C0M256:C:6900:0:3651=deu:0:0:28442:41985:1073:0
+NDR 1 Radio MV;ARD NDR:426000:C0M256:C:6900:0:3661=deu:0:0:28443:41985:1073:0
+NDR 1 Nieders.;ARD NDR:426000:C0M256:C:6900:0:3671=deu:0:0:28444:41985:1073:0
+NDR Info Spez.;ARD NDR:426000:C0M256:C:6900:0:3681=deu:0:0:28445:41985:1073:0
+Bremen Eins;ARD RB:426000:C0M256:C:6900:0:3701=deu:0:0:28448:41985:1073:0
+Nordwestradio;ARD RB:426000:C0M256:C:6900:0:3711=deu:0:0:28449:41985:1073:0
+Bremen Vier;ARD RB:426000:C0M256:C:6900:0:3721=deu:0:0:28450:41985:1073:0
+Inforadio;ARD rbb:426000:C0M256:C:6900:0:3801=deu:0:0:28452:41985:1073:0
+Kulturradio;ARD rbb:426000:C0M256:C:6900:0:3811=deu:0:0:28453:41985:1073:0
+Antenne Brandenburg;ARD rbb:426000:C0M256:C:6900:0:3821=deu:0:0:28454:41985:1073:0
+radioBERLIN 88,8;ARD rbb:426000:C0M256:C:6900:0:3831=deu:0:0:28455:41985:1073:0
+radioeins;ARD rbb:426000:C0M256:C:6900:0:3841=deu:0:0:28456:41985:1073:0
+Fritz;ARD rbb:426000:C0M256:C:6900:0:3851=deu:0:0:28457:41985:1073:0
+SWR 1 BW;ARD SWR:426000:C0M256:C:6900:0:4001=deu:0:0:28465:41985:1073:0
+SWR 1 RP;ARD SWR:426000:C0M256:C:6900:0:4011=deu:0:0:28466:41985:1073:0
+SWR 2;ARD SWR:426000:C0M256:C:6900:0:4021=deu;4022=deu:0:0:28467:41985:1073:0
+SWR 3;ARD SWR:426000:C0M256:C:6900:0:4031=deu:0:0:28468:41985:1073:0
+SWR 4 BW;ARD SWR:426000:C0M256:C:6900:0:4041=deu:0:0:28469:41985:1073:0
+SWR 4 RP;ARD SWR:426000:C0M256:C:6900:0:4051=deu:0:0:28470:41985:1073:0
+DASDING;ARD SWR:426000:C0M256:C:6900:0:4061=deu:0:0:28471:41985:1073:0
+SWR cont.ra;ARD SWR:426000:C0M256:C:6900:0:4071=deu:0:0:28472:41985:1073:0
+:More
+Unitymedia Infokanal;UnityDigitalTV:610000:C0M64:C:6900:513=2:514=deu:0:0:25101:9999:251:0
diff --git a/vdr/conf/channels.conf.dvb-s_astra b/vdr/conf/channels.conf.dvb-s_astra
new file mode 100644
index 0000000..6566ab0
--- /dev/null
+++ b/vdr/conf/channels.conf.dvb-s_astra
@@ -0,0 +1,128 @@
+:Main TV
+Das Erste;ARD:11836:hC34:S19.2E:27500:101:102=deu;106=deu:104:0:28106:1:1101:0
+ZDF;ZDFvision:11953:hC34:S19.2E:27500:110:120=deu,121=2ch;125=dd:130:0:28006:1:1079:0
+WDR Köln;ARD:11836:hC34:S19.2E:27500:601:602=deu:604:0:28111:1:1101:0
+SAT.1;ProSiebenSat.1:12480:vC34:S19.2E:27500:1791:1792=deu;1795=deu:34:0:46:133:33:0
+KABEL1;ProSiebenSat.1:12480:vC34:S19.2E:27500:511:512=deu:33:0:899:133:33:0
+ProSieben;ProSiebenSat.1:12480:vC34:S19.2E:27500:255:256=deu;257=deu:32:0:898:133:33:0
+RTL Television,RTL;RTL World:12187:hC34:S19.2E:27500:163:104=deu;106=deu:105:0:12003:1:1089:0
+RTL2;RTL World:12187:hC34:S19.2E:27500:166:128=deu:68:0:12020:1:1089:0
+Super RTL,S RTL;RTL World:12187:hC34:S19.2E:27500:165:120=deu:65:0:12040:1:1089:0
+VOX;RTL World:12187:hC34:S19.2E:27500:167:136=deu:71:0:12060:1:1089:0
+Bayerisches FS;ARD:11836:hC34:S19.2E:27500:201:202=deu:204:0:28107:1:1101:0
+hr-fernsehen;ARD:11836:hC34:S19.2E:27500:301:302=deu:304:0:28108:1:1101:0
+NDR FS NDS;ARD:12109:hC34:S19.2E:27500:2401:2402=deu:2404:0:28226:1:1073:0
+MDR FERNSEHEN;ARD:12109:hC34:S19.2E:27500:401:402=deu:404:0:28204:1:1073:0
+rbb Berlin;ARD:12109:hC34:S19.2E:27500:601:602=deu:604:0:28206:1:1073:0
+SR SÜDWEST Ferns.;ARD:12265:hC34:S19.2E:27500:1301:1302=deu:1304:0:28486:1:1093:0
+BR-alpha;ARD:11836:hC34:S19.2E:27500:701:702=deu:704:0:28112:1:1101:0
+Phoenix;ARD:11836:hC34:S19.2E:27500:901:902=deu:904:0:28114:1:1101:0
+arte;ARD:11836:hC34:S19.2E:27500:401:402=deu,403=fra:404:0:28109:1:1101:0
+3sat;ZDFvision:11953:hC34:S19.2E:27500:210:220=deu,221=2ch;225=dd:230:0:28007:1:1079:0
+KiKa;ZDFvision:11953:hC34:S19.2E:27500:310:320=deu:330:0:28008:1:1079:0
+ZDFdokukanal;ZDFvision:11953:hC34:S19.2E:27500:660:670=deu:630:0:28014:1:1079:0
+ZDFinfokanal;ZDFvision:11953:hC34:S19.2E:27500:610:620=deu:630:0:28011:1:1079:0
+ZDFtheaterkanal;ZDFvision:11953:hC34:S19.2E:27500:1110:1120=deu:630:0:28016:1:1079:0
+EinsPlus;ARD:12109:hC34:S19.2E:27500:301:302=deu:0:0:28203:1:1073:0
+EinsExtra;ARD:12109:hC34:S19.2E:27500:101:102=deu:0:0:28201:1:1073:0
+EinsFestival;ARD:12109:hC34:S19.2E:27500:201:202=deu:204:0:28202:1:1073:0
+:More TV
+DSF;BetaDigital:12480:vC34:S19.2E:27500:1023:1024=deu:39:0:900:133:33:0
+Eurosport;SES Astra:12226:hC34:S19.2E:27500:101+8190:103=deu:102:0:31200:1:1091:0
+n-tv;RTL World:12187:hC34:S19.2E:27500:169:73=deu:80:0:12090:1:1089:0
+N24;ProSiebenSat.1:12480:vC34:S19.2E:27500:2047:2048=deu:36:0:47:133:33:0
+EuroNews;GlobeCast:12226:hC34:S19.2E:27500:2432:2433=deu,2434=eng,2435=fra,2436=ita,2437=esl,2438=por,2439=rus,2440:0:0:31220:1:1091:0
+9Live;BetaDigital:12480:vC34:S19.2E:27500:767:768=deu:35:0:897:133:33:0
+XXP;T-Systems:12633:hC56:S19.2E:22000:203:303=deu:503:0:12602:1:1113:0
+:Music
+MTV Germany;MTV Networks:11973:V:S19.2E:27500:4031:4032:4034:0:28673:1:1078:0
+NICK;MTV Networks:11973:vC34:S19.2E:27500:4101+8190:4102=deu:4104:0:28680:1:1078:0
+VIVA;MTV Networks:11973:vC34:S19.2E:27500:4061+8190:4062:4064:0:28676:1:1078:0
+VIVA PLUS;MTV Networks:11973:vC34:S19.2E:27500:4071+8190:4072:4074:0:28677:1:1078:0
+:@100 Radio
+Antenne Brandenburg;ARD rbb:12265:hC34:S19.2E:27500:0:821=deu:0:0:28454:1:1093:0
+Bayern 1;ARD BR:12265:hC34:S19.2E:27500:0:101=deu:0:0:28400:1:1093:0
+Bayern2Radio;ARD BR:12265:hC34:S19.2E:27500:0:111=deu:0:0:28401:1:1093:0
+Bayern 3;ARD BR:12265:hC34:S19.2E:27500:0:121=deu:0:0:28402:1:1093:0
+Bayern 4 Klassik;ARD BR:12265:hC34:S19.2E:27500:0:131=deu;132=deu:0:0:28403:1:1093:0
+B5 aktuell;ARD BR:12265:hC34:S19.2E:27500:0:141=deu:0:0:28404:1:1093:0
+BAYERN MOBIL;ARD BR:12265:hC34:S19.2E:27500:0:151=deu:0:0:28405:1:1093:0
+BR Verkehrskanal;ARD BR:12265:hC34:S19.2E:27500:0:171=deu:0:0:28407:1:1093:0
+Bremen Eins;ARD RB:12265:hC34:S19.2E:27500:0:701=deu:0:0:28448:1:1093:0
+Bremen Vier;ARD RB:12265:hC34:S19.2E:27500:0:721=deu:0:0:28450:1:1093:0
+cont.ra;ARD SWR:12265:hC34:S19.2E:27500:0:1071=deu:0:0:28472:1:1093:0
+DASDING;ARD SWR:12265:hC34:S19.2E:27500:0:1061=deu:0:0:28471:1:1093:0
+DAS MODUL;ARD BR:12265:hC34:S19.2E:27500:0:161=deu:0:0:28406:1:1093:0
+DLF;ZDFvision:11953:hC34:S19.2E:27500:0:810=deu:0:0:28013:1:1079:0
+DKULTUR;ZDFvision:11953:hC34:S19.2E:27500:0:710=deu:0:0:28012:1:1079:0
+DW 1;ARD DW:12265:hC34:S19.2E:27500:0:301:0:0:28416:1:1093:0
+DW 4;ARD DW:12265:hC34:S19.2E:27500:0:311:0:0:28417:1:1093:0
+Eins Live;ARD WDR:12265:hC34:S19.2E:27500:0:1101=deu:0:0:28475:1:1093:0
+Eins Live diggi;ARD WDR:12265:hC34:S19.2E:27500:0:1161=deu:0:0:28481:1:1093:0
+Fritz;ARD rbb:12265:hC34:S19.2E:27500:0:851=deu:0:0:28457:1:1093:0
+hr1;ARD HR:12265:hC34:S19.2E:27500:0:401=deu:0:0:28419:1:1093:0
+hr2;ARD HR:12265:hC34:S19.2E:27500:0:411=deu;412=deu:0:0:28420:1:1093:0
+hr3;ARD HR:12265:hC34:S19.2E:27500:0:421=deu:0:0:28421:1:1093:0
+hr4;ARD HR:12265:hC34:S19.2E:27500:0:431=deu:0:0:28422:1:1093:0
+hr-info;ARD HR:12265:hC34:S19.2E:27500:0:461=deu:0:0:28424:1:1093:0
+hr-info +;ARD HR:12265:hC34:S19.2E:27500:0:471=deu:0:0:28425:1:1093:0
+InfoRadio;ARD rbb:12265:hC34:S19.2E:27500:0:801=deu:0:0:28452:1:1093:0
+Inselradio;T-Systems:12633:hC56:S19.2E:22000:0:702:0:0:12651:1:1113:0
+kulturradio;ARD rbb:12265:hC34:S19.2E:27500:0:811=deu:0:0:28453:1:1093:0
+MDR1 SACHSEN;ARD MDR:12265:hC34:S19.2E:27500:0:501=deu:0:0:28428:1:1093:0
+MDR1 S-ANHALT;ARD MDR:12265:hC34:S19.2E:27500:0:511=deu:0:0:28429:1:1093:0
+MDR1 THÜRINGEN;ARD MDR:12265:hC34:S19.2E:27500:0:521=deu:0:0:28430:1:1093:0
+MDR FIGARO;ARD MDR:12265:hC34:S19.2E:27500:0:531=deu:0:0:28431:1:1093:0
+MDR JUMP;ARD MDR:12265:hC34:S19.2E:27500:0:541=deu:0:0:28432:1:1093:0
+MDR INFO;ARD MDR:12265:hC34:S19.2E:27500:0:561=deu:0:0:28434:1:1093:0
+NDR 2;ARD NDR:12265:hC34:S19.2E:27500:0:601=deu:0:0:28437:1:1093:0
+NDR Kultur;ARD NDR:12265:hC34:S19.2E:27500:0:611=deu:0:0:28438:1:1093:0
+NDR Info;ARD NDR:12265:hC34:S19.2E:27500:0:621=deu:0:0:28439:1:1093:0
+NDR 90,3;ARD NDR:12265:hC34:S19.2E:27500:0:641=deu:0:0:28441:1:1093:0
+NDR 1 Welle Nord;ARD NDR:12265:hC34:S19.2E:27500:0:651=deu:0:0:28442:1:1093:0
+NDR 1 Radio MV;ARD NDR:12265:hC34:S19.2E:27500:0:661=deu:0:0:28443:1:1093:0
+NDR 1 Niedersachsen;ARD NDR:12265:hC34:S19.2E:27500:0:671=deu:0:0:28444:1:1093:0
+NDR Info Spezial;ARD NDR:12265:hC34:S19.2E:27500:0:681=deu:0:0:28445:1:1093:0
+Nordwestradio;ARD RB:12265:hC34:S19.2E:27500:0:711=deu:0:0:28449:1:1093:0
+N-JOY;ARD NDR:12265:hC34:S19.2E:27500:0:631=deu:0:0:28440:1:1093:0
+radioBERLIN 88,8;ARD rbb:12265:hC34:S19.2E:27500:0:831=deu:0:0:28455:1:1093:0
+radioeins;ARD rbb:12265:hC34:S19.2E:27500:0:841=deu:0:0:28456:1:1093:0
+radiomultikulti;ARD rbb:12265:hC34:S19.2E:27500:0:861=deu:0:0:28458:1:1093:0
+radio top40;T-Systems:12633:hC56:S19.2E:22000:0:353=deu:0:0:12653:1:1113:0
+SPUTNIK;ARD MDR:12265:hC34:S19.2E:27500:0:551=deu:0:0:28433:1:1093:0
+SR1;ARD SR:12265:hC34:S19.2E:27500:0:901=deu:0:0:28461:1:1093:0
+SR2;ARD SR:12265:hC34:S19.2E:27500:0:911=deu:0:0:28462:1:1093:0
+SR3;ARD SR:12265:hC34:S19.2E:27500:0:921=deu:0:0:28463:1:1093:0
+SWR1 BW;ARD SWR:12265:hC34:S19.2E:27500:0:1001=deu:0:0:28465:1:1093:0
+SWR1 RP;ARD SWR:12265:hC34:S19.2E:27500:0:1011=deu:0:0:28466:1:1093:0
+SWR2;ARD SWR:12265:hC34:S19.2E:27500:0:1021=deu;1022=deu:0:0:28467:1:1093:0
+SWR3;ARD SWR:12265:hC34:S19.2E:27500:0:1031=deu:0:0:28468:1:1093:0
+SWR4 BW;ARD SWR:12265:hC34:S19.2E:27500:0:1041=deu:0:0:28469:1:1093:0
+SWR4 RP;ARD SWR:12265:hC34:S19.2E:27500:0:1051=deu:0:0:28470:1:1093:0
+WDR 2;ARD WDR:12265:hC34:S19.2E:27500:0:1111=deu:0:0:28476:1:1093:0
+WDR 3;ARD WDR:12265:hC34:S19.2E:27500:0:1121=deu:0:0:28477:1:1093:0
+WDR 4;ARD WDR:12265:hC34:S19.2E:27500:0:1131=deu:0:0:28478:1:1093:0
+WDR 5;ARD WDR:12265:hC34:S19.2E:27500:0:1141=deu;1142=deu:0:0:28479:1:1093:0
+WDR 2 Klassik;ARD WDR:12265:hC34:S19.2E:27500:0:1171=deu:0:0:28482:1:1093:0
+WDR Event;ARD WDR:12265:hC34:S19.2E:27500:0:1181=deu:0:0:28483:1:1093:0
+WDR Funkhaus Europa;ARD WDR:12265:hC34:S19.2E:27500:0:1151=deu:0:0:28480:1:1093:0
+YOU FM;ARD HR:12265:hC34:S19.2E:27500:0:451=deu:0:0:28423:1:1093:0
+:@200 die dritten
+MDR S-ANHALT;ARD:12109:hC34:S19.2E:27500:401:402=deu:404:0:28229:1:1073:0
+MDR SACHSEN;ARD:12109:hC34:S19.2E:27500:401:402=deu:404:0:28228:1:1073:0
+MDR THÜRINGEN;ARD:12109:hC34:S19.2E:27500:401:402=deu:404:0:28230:1:1073:0
+NDR FS HH;ARD:12109:hC34:S19.2E:27500:2401:2402=deu:2404:0:28225:1:1073:0
+NDR FS SH;ARD:12109:hC34:S19.2E:27500:2401:2402=deu:2404:0:28227:1:1073:0
+NDR FS MV;ARD:12109:hC34:S19.2E:27500:2401:2402=deu:2404:0:28224:1:1073:0
+rbb Brandenburg;ARD:12109:hC34:S19.2E:27500:601:602=deu:604:0:28205:1:1073:0
+SÜDWEST Ferns. BW;ARD:11836:hC34:S19.2E:27500:801:802=deu:804:0:28113:1:1101:0
+SÜDWEST Ferns. RP;ARD:12109:hC34:S19.2E:27500:3101:3102=deu:3104:0:28231:1:1073:0
+WDR Aachen;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28305:1:1201:0
+WDR Bielefeld;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28306:1:1201:0
+WDR Dortmund;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28307:1:1201:0
+WDR Düsseldorf;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28308:1:1201:0
+WDR Essen;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28309:1:1201:0
+WDR Münster;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28310:1:1201:0
+WDR Siegen;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28311:1:1201:0
+WDR Wuppertal;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28312:1:1201:0
+:@1000 New channels
diff --git a/vdr/conf/keymacros.conf b/vdr/conf/keymacros.conf
new file mode 100644
index 0000000..3b5d863
--- /dev/null
+++ b/vdr/conf/keymacros.conf
@@ -0,0 +1,22 @@
+# Remote control key macros for VDR
+#
+# Format:
+#
+# macrokey key1 key2 key3...
+# macrokey @plugin key1 key2 key3...
+#
+# See man vdr(5)
+
+Red Recordings
+Green Channels
+Yellow Schedule
+Blue Timers
+User1 @pilotskin
+User2 @epgsearch
+User3 @extrecmenu
+User4 Info
+User5 @music
+User6 Commands
+User7 @osdteletext
+User8 @sleeptimer
+User9 @screenshot \ No newline at end of file
diff --git a/vdr/conf/noad-exclude-channels.conf b/vdr/conf/noad-exclude-channels.conf
new file mode 100644
index 0000000..0b0ea5e
--- /dev/null
+++ b/vdr/conf/noad-exclude-channels.conf
@@ -0,0 +1,53 @@
+PREMIERE START
+PREMIERE 1
+PREMIERE 2
+PREMIERE 3
+PREMIERE 4
+PREMIERE Austria
+PREMIERE DIREKT
+D 1
+D 2
+D 3
+D 4
+D 5
+D 6
+D 7
+D 8
+D 9
+arena home (Premiere)
+PREMIERE SPORT PORTAL
+Bundesliga
+Champ. League
+Euroliga
+Fussball
+Motorsport
+Wrestling
+News
+SCI FI
+MGM
+DISNEY CHANNEL
+JETIX
+JUNIOR
+DISCOVERY CHANNEL
+ANIMAL PLANET
+DISCOVERY GESCHICHTE
+FOCUS GESUNDHEIT
+PREMIERE SERIE
+PREMIERE KRIMI
+PREMIERE FILMCLASSICS
+PREMIERE FILMFEST
+PREMIERE NOSTALGIE
+13 TH STREET
+HIT24
+CLASSICA
+GOLDSTAR TV
+HEIMATKANAL
+BIG BROTHER
+THEMA LIVE
+BEATE-UHSE.TV
+EROTIK
+Erotik 1
+Erotik 2
+BLUE MOVIE
+BLUE MOVIE Extra
+BLUE MOVIE Gay
diff --git a/vdr/conf/remote.conf b/vdr/conf/remote.conf
new file mode 100644
index 0000000..ec40702
--- /dev/null
+++ b/vdr/conf/remote.conf
@@ -0,0 +1,180 @@
+KBD.Up 00000000001B5B41
+KBD.Down 00000000001B5B42
+KBD.Menu 000000000000006D
+KBD.Ok 000000000000000D
+KBD.Back 000000000000007F
+KBD.Left 00000000001B5B44
+KBD.Right 00000000001B5B43
+KBD.Red 000000001B5B5B41
+KBD.Green 000000001B5B5B42
+KBD.Yellow 000000001B5B5B43
+KBD.Blue 000000001B5B5B44
+KBD.0 0000000000000030
+KBD.1 0000000000000031
+KBD.2 0000000000000032
+KBD.3 0000000000000033
+KBD.4 0000000000000034
+KBD.5 0000000000000035
+KBD.6 0000000000000036
+KBD.7 0000000000000037
+KBD.8 0000000000000038
+KBD.9 0000000000000039
+KBD.Volume+ 000000000000002B
+KBD.Volume- 000000000000002D
+KBD.Mute 0000000000000073
+remote-event2._Setup /proc/av7110_ir 00000000 31
+remote-event2.Up 0000000100010021
+remote-event2.Down 0000000100010022
+remote-event2.Menu 000000010001000E
+remote-event2.Ok 0000000100010026
+remote-event2.Back 0000000100010020
+remote-event2.Left 0000000100010012
+remote-event2.Right 0000000100010011
+remote-event2.Red 000000010001000C
+remote-event2.Green 000000010001002F
+remote-event2.Yellow 0000000100010039
+remote-event2.Blue 000000010001002A
+remote-event2.0 0000000100010001
+remote-event2.1 0000000100010002
+remote-event2.2 0000000100010003
+remote-event2.3 0000000100010004
+remote-event2.4 0000000100010005
+remote-event2.5 0000000100010006
+remote-event2.6 0000000100010007
+remote-event2.7 0000000100010008
+remote-event2.8 0000000100010009
+remote-event2.9 000000010001000A
+remote-event2.Play 0000000100010036
+remote-event2.Pause 0000000100010031
+remote-event2.Stop 0000000100010037
+remote-event2.Record 0000000100010038
+remote-event2.FastFwd 0000000100010035
+remote-event2.FastRew 0000000100010033
+remote-event2.Channel+ 000000010001001F
+remote-event2.Channel- 0000000100010025
+remote-event2.Volume+ 000000010001003D
+remote-event2.Volume- 000000010001000D
+remote-event2.Mute 0000000100010010
+remote-event2.Power 000000010001003C
+LIRC.Up Up
+LIRC.Down Down
+LIRC.Menu Menu
+LIRC.Ok Ok
+LIRC.Back Back
+LIRC.Left Left
+LIRC.Right Right
+LIRC.Red Red
+LIRC.Green Green
+LIRC.Yellow Yellow
+LIRC.Blue Blue
+LIRC.0 0
+LIRC.1 1
+LIRC.2 2
+LIRC.3 3
+LIRC.4 4
+LIRC.5 5
+LIRC.6 6
+LIRC.7 7
+LIRC.8 8
+LIRC.9 9
+LIRC.Info Info
+LIRC.Play Play
+LIRC.Pause Pause
+LIRC.Stop Stop
+LIRC.Record Record
+LIRC.FastFwd FastFwd
+LIRC.FastRew FastRew
+LIRC.Next Next
+LIRC.Prev Prev
+LIRC.User1 User1
+LIRC.User2 User2
+LIRC.User3 User3
+LIRC.User4 User4
+LIRC.User5 User5
+LIRC.User6 User6
+LIRC.User7 User7
+LIRC.User8 User8
+LIRC.User9 User9
+LIRC.Channel+ Channel+
+LIRC.Channel- Channel-
+LIRC.Volume+ Volume+
+LIRC.Volume- Volume-
+LIRC.Mute Mute
+LIRC.Audio Audio
+LIRC.Power Power
+softdevice-xv.Up 000000000000FF52
+softdevice-xv.Down 000000000000FF54
+softdevice-xv.Menu 000000000000006D
+softdevice-xv.Ok 000000000000FF0D
+softdevice-xv.Back 000000000000FF08
+softdevice-xv.Left 000000000000FF51
+softdevice-xv.Right 000000000000FF53
+softdevice-xv.Red 000000000000FFBE
+softdevice-xv.Green 000000000000FFBF
+softdevice-xv.Yellow 000000000000FFC0
+softdevice-xv.Blue 000000000000FFC1
+softdevice-xv.0 0000000000000030
+softdevice-xv.1 0000000000000031
+softdevice-xv.2 0000000000000032
+softdevice-xv.3 0000000000000033
+softdevice-xv.4 0000000000000034
+softdevice-xv.5 0000000000000035
+softdevice-xv.6 0000000000000036
+softdevice-xv.7 0000000000000037
+softdevice-xv.8 0000000000000038
+softdevice-xv.9 0000000000000039
+softdevice-xv.Channel+ 000000000000FF55
+softdevice-xv.Channel- 000000000000FF56
+softdevice-xv.Volume+ 000000000000FFAB
+softdevice-xv.Volume- 000000000000FFAD
+softdevice-xv.Mute 000000000000FFAA
+softdevice-xv.Audio 000000000000FFAF
+softdevice-xv.User1 000000000000FFC2
+softdevice-xv.User2 000000000000FFC3
+softdevice-xv.User3 000000000000FFC4
+softdevice-xv.User4 000000000000FFC5
+softdevice-xv.User5 000000000000FFC6
+softdevice-xv.User6 000000000000FFC7
+softdevice-xv.User7 000000000000FFC8
+softdevice-xv.User8 000000000000FFC9
+XKeySym.Up Up
+XKeySym.Down Down
+XKeySym.Menu m
+XKeySym.Ok Return
+XKeySym.Back BackSpace
+XKeySym.Left Left
+XKeySym.Right Right
+XKeySym.Red F1
+XKeySym.Green F2
+XKeySym.Yellow F3
+XKeySym.Blue F4
+XKeySym.0 0
+XKeySym.1 1
+XKeySym.2 2
+XKeySym.3 3
+XKeySym.4 4
+XKeySym.5 5
+XKeySym.6 6
+XKeySym.7 7
+XKeySym.8 8
+XKeySym.9 9
+XKeySym.Info i
+XKeySym.Play w
+XKeySym.Pause p
+XKeySym.Stop s
+XKeySym.FastFwd v
+XKeySym.FastRew c
+XKeySym.Channel+ Prior
+XKeySym.Channel- Next
+XKeySym.Volume+ KP_Subtract
+XKeySym.Volume- KP_Add
+XKeySym.Mute KP_Multiply
+XKeySym.Audio KP_Divide
+XKeySym.User1 F5
+XKeySym.User2 F6
+XKeySym.User3 F7
+XKeySym.User4 F8
+XKeySym.User5 F9
+XKeySym.User6 F10
+XKeySym.User7 F11
+XKeySym.User8 F12
diff --git a/vdr/conf/shutdown.conf b/vdr/conf/shutdown.conf
new file mode 100644
index 0000000..bb55a41
--- /dev/null
+++ b/vdr/conf/shutdown.conf
@@ -0,0 +1,68 @@
+# Global config file for vdrshutdown
+
+# Specify which wakeup-mode you prefer (0=none/1=acpi-wakeup/2=nvram-wakeup/3=extension-board)
+WAKEUP_MODE="0"
+
+
+### nvram-wakeup
+##
+# Read board configuration from specified configuration file (e.g: /etc/nvram-wakeup.conf)
+NVRAM_CONFIG=""
+
+# Specify the iw (infowriter) name (e.g: gigabyte_5aa)
+NVRAM_IWNAME=""
+
+# Try "nvram-wakeup --help", don't use --wakeupbefore here
+NVRAM_OPT=""
+
+# Needs your mainboard a reboot? (0=false/1=true/2=allways)
+NEED_REBOOT="0"
+
+# If you use grub-set-default X, enter (0-9) for X
+GRUB_POWEROFF=2
+
+# Do you use lilo instead of grub? (0=false/1=true)
+LILO="0"
+
+
+### acpi-wakeup, nvram-wakeup and extension-board
+##
+# Specify how many minutes before the programmed wakeuptime the computer should wake up
+WAKEUP_BEFORE=5
+
+# Set this to "0", "MAXIMAL", "INTERVAL" or your max-power-off-time in minutes
+MAX_POWEROFF_TIME="0"
+
+# If you has set MAX_POWEROFF_TIME to "INTERVAL", specify the interval in days and your dreamtime in hours:minutes
+INTERVAL="3" # days
+DREAMTIME="03:00" # hours:minutes
+
+# Action if $MIN_WAKEUPTIME -gt $WAKEUPTIME
+MIN_WAKEUP_VAR="0"
+
+# Waiting period in minutes if CHECK_PROCS is active
+WAITING_PERIOD="15"
+
+# to abort the shutdown
+#
+# format: <tests> && <osd + logger message>
+#
+CHECK_PROCS=\
+"
+test -s $VDRCONFDIR/plugins/queue.vdrrip && echo 'vdrrip laeuft'
+pidof -x vdr-no-shutdown >/dev/null 2>&1 && echo 'vdr-no-shutdown laeuft'
+pidof -x updateepg.sh >/dev/null 2>&1 && echo 'updateepg laeuft'
+pidof -x tvm2vdr.pl >/dev/null 2>&1 && echo 'tvmovie2vdr laeuft'
+pidof infosatepg >/dev/null 2>&1 && echo 'infosatepg laeuft'
+pidof cutinplace >/dev/null 2>&1 && echo 'cutinplace laeuft'
+pidof transcode >/dev/null 2>&1 && echo 'transcode laeuft'
+pidof vcdimager >/dev/null 2>&1 && echo 'vcdimager laeuft'
+pidof mencoder >/dev/null 2>&1 && echo 'mencoder laeuft'
+pidof tosvcd >/dev/null 2>&1 && echo 'tosvcd laeuft'
+pidof lame >/dev/null 2>&1 && echo 'lame laeuft'
+pidof noad >/dev/null 2>&1 && echo 'noad laeuft'
+netstat -n|grep ^tcp|egrep -q ':22 ' && echo 'ssh client(s) connected'
+netstat -n|grep ^tcp|egrep -q ':139 ' && echo 'smb client(s) connected'
+netstat -n|grep ^tcp|egrep -q ':2004 ' && echo 'streamdev client(s) connected'
+grep -qs ^ /var/lib/nfs/rmtab && echo 'nfs client(s) connected'
+"
diff --git a/vdr/extensions/vdr-1.6.0-2-ext_dvb-api.diff b/vdr/extensions/vdr-1.6.0-2-ext_dvb-api.diff
new file mode 100644
index 0000000..d7aedf6
--- /dev/null
+++ b/vdr/extensions/vdr-1.6.0-2-ext_dvb-api.diff
@@ -0,0 +1,14 @@
+diff -ruNp vdr-1.6.0-2-extensions/dvbdevice.h~ vdr-1.6.0-2-ext-softosd/dvbdevice.h
+--- vdr-1.6.0-2-extensions/dvbdevice.h~ 2009-01-09 20:14:49.000000000 +0100
++++ vdr-1.6.0-2-extensions/dvbdevice.h 2009-01-09 20:13:53.000000000 +0100
+@@ -15,8 +15,8 @@
+ #include "device.h"
+ #include "dvbspu.h"
+
+-#if DVB_API_VERSION != 3
+-#error VDR requires Linux DVB driver API version 3!
++#if DVB_API_VERSION < 3
++#error VDR requires Linux DVB driver API version 3 or greater!
+ #endif
+
+ #define MAXDVBDEVICES 8
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);
diff --git a/vdr/extensions/vdr-1.7.0-ext_h264-s2ng-speedup.diff b/vdr/extensions/vdr-1.7.0-ext_h264-s2ng-speedup.diff
new file mode 100644
index 0000000..b9e1beb
--- /dev/null
+++ b/vdr/extensions/vdr-1.7.0-ext_h264-s2ng-speedup.diff
@@ -0,0 +1,5483 @@
+diff -ruNp vdr-1.7.0-extensions/channels.c vdr-1.7.0-ext-h264-s2ng-speedup/channels.c
+--- vdr-1.7.0-extensions/channels.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/channels.c 2009-04-12 23:52:16.000000000 +0200
+@@ -4,7 +4,7 @@
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+- * $Id: channels.c 2.2 2008/04/12 13:49:12 kls Exp $
++ * $Id: channels.c 2.5 2009/04/10 11:29:55 kls Exp $
+ */
+
+ #include "channels.h"
+@@ -24,114 +24,86 @@
+ // --- Channel Parameter Maps ------------------------------------------------
+
+ const tChannelParameterMap InversionValues[] = {
+- { 0, DVBFE_INVERSION_OFF, trNOOP("off") },
+- { 1, DVBFE_INVERSION_ON, trNOOP("on") },
+- { 999, DVBFE_INVERSION_AUTO },
++ { 0, INVERSION_OFF, trNOOP("off") },
++ { 1, INVERSION_ON, trNOOP("on") },
++ { 999, INVERSION_AUTO, trNOOP("auto") },
+ { -1 }
+ };
+
+ const tChannelParameterMap BandwidthValues[] = {
+- { 5, DVBFE_BANDWIDTH_5_MHZ, "5 MHz" },
+- { 6, DVBFE_BANDWIDTH_6_MHZ, "6 MHz" },
+- { 7, DVBFE_BANDWIDTH_7_MHZ, "7 MHz" },
+- { 8, DVBFE_BANDWIDTH_8_MHZ, "8 MHz" },
+- { 999, DVBFE_BANDWIDTH_AUTO },
++ { 6, 6000000, "6 MHz" },
++ { 7, 7000000, "7 MHz" },
++ { 8, 8000000, "8 MHz" },
+ { -1 }
+ };
+
+ const tChannelParameterMap CoderateValues[] = {
+- { 0, DVBFE_FEC_NONE, trNOOP("none") },
+- { 12, DVBFE_FEC_1_2, "1/2" },
+- { 13, DVBFE_FEC_1_3, "1/3" },
+- { 14, DVBFE_FEC_1_4, "1/4" },
+- { 23, DVBFE_FEC_2_3, "2/3" },
+- { 25, DVBFE_FEC_2_5, "2/5" },
+- { 34, DVBFE_FEC_3_4, "3/4" },
+- { 35, DVBFE_FEC_3_5, "3/5" },
+- { 45, DVBFE_FEC_4_5, "4/5" },
+- { 56, DVBFE_FEC_5_6, "5/6" },
+- { 67, DVBFE_FEC_6_7, "6/7" },
+- { 78, DVBFE_FEC_7_8, "7/8" },
+- { 89, DVBFE_FEC_8_9, "8/9" },
+- { 910, DVBFE_FEC_9_10, "9/10" },
+- { 999, DVBFE_FEC_AUTO },
++ { 0, FEC_NONE, trNOOP("none") },
++ { 12, FEC_1_2, "1/2" },
++ { 23, FEC_2_3, "2/3" },
++ { 34, FEC_3_4, "3/4" },
++ { 35, FEC_3_5, "3/5" },
++ { 45, FEC_4_5, "4/5" },
++ { 56, FEC_5_6, "5/6" },
++ { 67, FEC_6_7, "6/7" },
++ { 78, FEC_7_8, "7/8" },
++ { 89, FEC_8_9, "8/9" },
++ { 910, FEC_9_10, "9/10" },
++ { 999, FEC_AUTO, trNOOP("auto") },
+ { -1 }
+ };
+
+ const tChannelParameterMap ModulationValues[] = {
+- { 0, DVBFE_MOD_NONE, trNOOP("none") },
+- { 4, DVBFE_MOD_QAM4, "QAM4" },
+- { 16, DVBFE_MOD_QAM16, "QAM16" },
+- { 32, DVBFE_MOD_QAM32, "QAM32" },
+- { 64, DVBFE_MOD_QAM64, "QAM64" },
+- { 128, DVBFE_MOD_QAM128, "QAM128" },
+- { 256, DVBFE_MOD_QAM256, "QAM256" },
+- { 512, DVBFE_MOD_QAM512, "QAM512" },
+- {1024, DVBFE_MOD_QAM1024, "QAM1024" },
+- { 1, DVBFE_MOD_BPSK, "BPSK" },
+- { 2, DVBFE_MOD_QPSK, "QPSK" },
+- { 3, DVBFE_MOD_OQPSK, "OQPSK" },
+- { 5, DVBFE_MOD_8PSK, "8PSK" },
+- { 6, DVBFE_MOD_16APSK, "16APSK" },
+- { 7, DVBFE_MOD_32APSK, "32APSK" },
+- { 8, DVBFE_MOD_OFDM, "OFDM" },
+- { 9, DVBFE_MOD_COFDM, "COFDM" },
+- { 10, DVBFE_MOD_VSB8, "VSB8" },
+- { 11, DVBFE_MOD_VSB16, "VSB16" },
+- { 998, DVBFE_MOD_QAMAUTO, "QAMAUTO" },
+- { 999, DVBFE_MOD_AUTO },
++ { 16, QAM_16, "QAM16" },
++ { 32, QAM_32, "QAM32" },
++ { 64, QAM_64, "QAM64" },
++ { 128, QAM_128, "QAM128" },
++ { 256, QAM_256, "QAM256" },
++ { 2, QPSK, "QPSK" },
++ { 5, PSK_8, "8PSK" },
++ { 6, APSK_16, "16APSK" },
++ { 10, VSB_8, "VSB8" },
++ { 11, VSB_16, "VSB16" },
++ { 998, QAM_AUTO, "QAMAUTO" },
+ { -1 }
+ };
+
+ const tChannelParameterMap SystemValues[] = {
+- { 0, DVBFE_DELSYS_DVBS, "DVB-S" },
+- { 1, DVBFE_DELSYS_DVBS2, "DVB-S2" },
++ { 0, SYS_DVBS, "DVB-S" },
++ { 1, SYS_DVBS2, "DVB-S2" },
+ { -1 }
+ };
+
+ const tChannelParameterMap TransmissionValues[] = {
+- { 2, DVBFE_TRANSMISSION_MODE_2K, "2K" },
+- { 4, DVBFE_TRANSMISSION_MODE_4K, "4K" },
+- { 8, DVBFE_TRANSMISSION_MODE_8K, "8K" },
+- { 999, DVBFE_TRANSMISSION_MODE_AUTO },
++ { 2, TRANSMISSION_MODE_2K, "2K" },
++ { 8, TRANSMISSION_MODE_8K, "8K" },
++ { 999, TRANSMISSION_MODE_AUTO, trNOOP("auto") },
+ { -1 }
+ };
+
+ const tChannelParameterMap GuardValues[] = {
+- { 4, DVBFE_GUARD_INTERVAL_1_4, "1/4" },
+- { 8, DVBFE_GUARD_INTERVAL_1_8, "1/8" },
+- { 16, DVBFE_GUARD_INTERVAL_1_16, "1/16" },
+- { 32, DVBFE_GUARD_INTERVAL_1_32, "1/32" },
+- { 999, DVBFE_GUARD_INTERVAL_AUTO },
++ { 4, GUARD_INTERVAL_1_4, "1/4" },
++ { 8, GUARD_INTERVAL_1_8, "1/8" },
++ { 16, GUARD_INTERVAL_1_16, "1/16" },
++ { 32, GUARD_INTERVAL_1_32, "1/32" },
++ { 999, GUARD_INTERVAL_AUTO, trNOOP("auto") },
+ { -1 }
+ };
+
+ const tChannelParameterMap HierarchyValues[] = {
+- { 0, DVBFE_HIERARCHY_OFF, trNOOP("off") },
+- { 1, DVBFE_HIERARCHY_ON, trNOOP("on") },
+- { 999, DVBFE_HIERARCHY_AUTO },
+- { -1 }
+- };
+-
+-const tChannelParameterMap AlphaValues[] = {
+- { 0, 0 },
+- { 1, DVBFE_ALPHA_1 },
+- { 2, DVBFE_ALPHA_2 },
+- { 4, DVBFE_ALPHA_4 },
+- { -1 }
+- };
+-
+-const tChannelParameterMap PriorityValues[] = {
+- { 0, DVBFE_STREAM_PRIORITY_HP, trNOOP("high") },
+- { 1, DVBFE_STREAM_PRIORITY_LP, trNOOP("low") },
++ { 0, HIERARCHY_NONE, trNOOP("none") },
++ { 1, HIERARCHY_1, "1" },
++ { 2, HIERARCHY_2, "2" },
++ { 4, HIERARCHY_4, "4" },
++ { 999, HIERARCHY_AUTO, trNOOP("auto") },
+ { -1 }
+ };
+
+ const tChannelParameterMap RollOffValues[] = {
+- { 0, DVBFE_ROLLOFF_UNKNOWN },
+- { 20, DVBFE_ROLLOFF_20, "0.20" },
+- { 25, DVBFE_ROLLOFF_25, "0.25" },
+- { 35, DVBFE_ROLLOFF_35, "0.35" },
++ { 0, ROLLOFF_AUTO, trNOOP("auto") },
++ { 20, ROLLOFF_20, "0.20" },
++ { 25, ROLLOFF_25, "0.25" },
++ { 35, ROLLOFF_35, "0.35" },
+ { -1 }
+ };
+
+@@ -223,22 +195,20 @@ cChannel::cChannel(void)
+ pluginParam = strdup("");
+ #endif /* PLUGINPARAM */
+ memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);
+- inversion = DVBFE_INVERSION_AUTO;
+- bandwidth = DVBFE_BANDWIDTH_AUTO;
+- coderateH = DVBFE_FEC_AUTO;
+- coderateL = DVBFE_FEC_AUTO;
+- modulation = DVBFE_MOD_AUTO;
+- system = DVBFE_DELSYS_DVBS;
+- transmission = DVBFE_TRANSMISSION_MODE_AUTO;
+- guard = DVBFE_GUARD_INTERVAL_AUTO;
+- hierarchy = DVBFE_HIERARCHY_AUTO;
+- alpha = 0;
+- priority = DVBFE_STREAM_PRIORITY_HP;
+- rollOff = DVBFE_ROLLOFF_UNKNOWN;
++ inversion = INVERSION_AUTO;
++ bandwidth = 8000000;
++ coderateH = FEC_AUTO;
++ coderateL = FEC_AUTO;
++ modulation = QPSK;
++ system = SYS_DVBS;
++ transmission = TRANSMISSION_MODE_AUTO;
++ guard = GUARD_INTERVAL_AUTO;
++ hierarchy = HIERARCHY_AUTO;
++ rollOff = ROLLOFF_AUTO;
+ modification = CHANNELMOD_NONE;
+ schedule = NULL;
+ linkChannels = NULL;
+- refChannel = NULL;
++ refChannels = NULL;
+ }
+
+ cChannel::cChannel(const cChannel &Channel)
+@@ -252,28 +222,26 @@ cChannel::cChannel(const cChannel &Chann
+ #endif /* PLUGINPARAM */
+ schedule = NULL;
+ linkChannels = NULL;
+- refChannel = NULL;
++ refChannels = NULL;
+ *this = Channel;
+ }
+
+ cChannel::~cChannel()
+ {
+- delete linkChannels;
+- linkChannels = NULL; // more than one channel can link to this one, so we need the following loop
+- for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
+- if (Channel->linkChannels) {
+- for (cLinkChannel *lc = Channel->linkChannels->First(); lc; lc = Channel->linkChannels->Next(lc)) {
+- if (lc->Channel() == this) {
+- Channel->linkChannels->Del(lc);
+- break;
+- }
+- }
+- if (Channel->linkChannels->Count() == 0) {
+- delete Channel->linkChannels;
+- Channel->linkChannels = NULL;
+- }
+- }
+- }
++ if (linkChannels) {
++ // in all channels which we link to remove the reference to us
++ for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc))
++ lc->Channel()->DelRefChannel(this);
++ delete linkChannels;
++ linkChannels = NULL;
++ }
++ if (refChannels) {
++ // in all channels which reference us remove their link to us
++ for (cLinkChannel *lc = refChannels->First(); lc; lc = refChannels->Next(lc))
++ lc->Channel()->DelLinkChannel(this);
++ delete refChannels;
++ refChannels = NULL;
++ }
+ free(name);
+ free(shortName);
+ free(provider);
+@@ -350,8 +318,6 @@ void cChannel::CopyTransponderData(const
+ transmission = Channel->transmission;
+ guard = Channel->guard;
+ hierarchy = Channel->hierarchy;
+- alpha = Channel->alpha;
+- priority = Channel->priority;
+ rollOff = Channel->rollOff;
+ #ifdef USE_PLUGINPARAM
+ if (IsPlug()) pluginParam = strcpyrealloc(pluginParam, Channel->pluginParam);
+@@ -430,9 +396,9 @@ bool cChannel::SetCableTransponderData(i
+ return true;
+ }
+
+-bool cChannel::SetTerrTransponderData(int Source, int Frequency, int Bandwidth, int Modulation, int Hierarchy, int CoderateH, int CoderateL, int Guard, int Transmission, int Alpha, int Priority)
++bool cChannel::SetTerrTransponderData(int Source, int Frequency, int Bandwidth, int Modulation, int Hierarchy, int CoderateH, int CoderateL, int Guard, int Transmission)
+ {
+- if (source != Source || frequency != Frequency || bandwidth != Bandwidth || modulation != Modulation || hierarchy != Hierarchy || coderateH != CoderateH || coderateL != CoderateL || guard != Guard || transmission != Transmission || alpha != Alpha || priority != Priority) {
++ if (source != Source || frequency != Frequency || bandwidth != Bandwidth || modulation != Modulation || hierarchy != Hierarchy || coderateH != CoderateH || coderateL != CoderateL || guard != Guard || transmission != Transmission) {
+ cString OldTransponderData = TransponderDataToString();
+ source = Source;
+ frequency = Frequency;
+@@ -443,8 +409,6 @@ bool cChannel::SetTerrTransponderData(in
+ coderateL = CoderateL;
+ guard = Guard;
+ transmission = Transmission;
+- alpha = Alpha;
+- priority = Priority;
+ schedule = NULL;
+ if (Number()) {
+ dsyslog("changing transponder data of channel %d from %s to %s", Number(), *OldTransponderData, *TransponderDataToString());
+@@ -558,10 +522,10 @@ static int IntArrayToString(char *s, con
+ return q - s;
+ }
+
+-void cChannel::SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid)
++void cChannel::SetPids(int Vpid, int Ppid, int Vtype, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid)
+ {
+ int mod = CHANNELMOD_NONE;
+- if (vpid != Vpid || ppid != Ppid || tpid != Tpid)
++ if (vpid != Vpid || ppid != Ppid || vtype != Vtype || tpid != Tpid)
+ mod |= CHANNELMOD_PIDS;
+ int m = IntArraysDiffer(apids, Apids, alangs, ALangs) | IntArraysDiffer(dpids, Dpids, dlangs, DLangs) | IntArraysDiffer(spids, Spids, slangs, SLangs);
+ if (m & STRDIFF)
+@@ -595,9 +559,10 @@ void cChannel::SetPids(int Vpid, int Ppi
+ q = NewSpidsBuf;
+ q += IntArrayToString(q, Spids, 10, SLangs);
+ *q = 0;
+- dsyslog("changing pids of channel %d from %d+%d:%s:%s:%d to %d+%d:%s:%s:%d", Number(), vpid, ppid, OldApidsBuf, OldSpidsBuf, tpid, Vpid, Ppid, NewApidsBuf, NewSpidsBuf, Tpid);
++ dsyslog("changing pids of channel %d from %d+%d=%d:%s:%s:%d to %d+%d=%d:%s:%s:%d", Number(), vpid, ppid, vtype, OldApidsBuf, OldSpidsBuf, tpid, Vpid, Ppid, Vtype, NewApidsBuf, NewSpidsBuf, Tpid);
+ vpid = Vpid;
+ ppid = Ppid;
++ vtype = Vtype;
+ for (int i = 0; i < MAXAPIDS; i++) {
+ apids[i] = Apids[i];
+ strn0cpy(alangs[i], ALangs[i], MAXLANGCODE2);
+@@ -674,7 +639,7 @@ void cChannel::SetLinkChannels(cLinkChan
+ q += sprintf(q, "linking channel %d from", Number());
+ if (linkChannels) {
+ for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) {
+- lc->Channel()->SetRefChannel(NULL);
++ lc->Channel()->DelRefChannel(this);
+ q += sprintf(q, " %d", lc->Channel()->Number());
+ }
+ delete linkChannels;
+@@ -685,7 +650,7 @@ void cChannel::SetLinkChannels(cLinkChan
+ linkChannels = LinkChannels;
+ if (linkChannels) {
+ for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) {
+- lc->Channel()->SetRefChannel(this);
++ lc->Channel()->AddRefChannel(this);
+ q += sprintf(q, " %d", lc->Channel()->Number());
+ //dsyslog("link %4d -> %4d: %s", Number(), lc->Channel()->Number(), lc->Channel()->Name());
+ }
+@@ -695,9 +660,39 @@ void cChannel::SetLinkChannels(cLinkChan
+ dsyslog(buffer);
+ }
+
+-void cChannel::SetRefChannel(cChannel *RefChannel)
++void cChannel::AddRefChannel(cChannel *RefChannel)
++{
++ if (!refChannels)
++ refChannels = new cLinkChannels;
++ refChannels->Add(new cLinkChannel(RefChannel));
++}
++
++void cChannel::DelRefChannel(cChannel *RefChannel)
++{
++ for (cLinkChannel *lc = refChannels->First(); lc; lc = refChannels->Next(lc)) {
++ if (lc->Channel() == RefChannel) {
++ refChannels->Del(lc);
++ if (refChannels->Count() <= 0) {
++ delete refChannels;
++ refChannels = NULL;
++ }
++ return;
++ }
++ }
++}
++
++void cChannel::DelLinkChannel(cChannel *LinkChannel)
+ {
+- refChannel = RefChannel;
++ for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) {
++ if (lc->Channel() == LinkChannel) {
++ linkChannels->Del(lc);
++ if (linkChannels->Count() <= 0) {
++ delete linkChannels;
++ linkChannels = NULL;
++ }
++ return;
++ }
++ }
+ }
+
+ static int PrintParameter(char *p, char Name, int Value)
+@@ -726,7 +721,6 @@ cString cChannel::ParametersToString(voi
+ char *q = buffer;
+ *q = 0;
+ ST(" S ") q += sprintf(q, "%c", polarization);
+- ST(" T") q += PrintParameter(q, 'A', MapToUser(alpha, AlphaValues));
+ ST(" T") q += PrintParameter(q, 'B', MapToUser(bandwidth, BandwidthValues));
+ ST("CST") q += PrintParameter(q, 'C', MapToUser(coderateH, CoderateValues));
+ ST(" T") q += PrintParameter(q, 'D', MapToUser(coderateL, CoderateValues));
+@@ -734,7 +728,6 @@ cString cChannel::ParametersToString(voi
+ ST("CST") q += PrintParameter(q, 'I', MapToUser(inversion, InversionValues));
+ ST("CST") q += PrintParameter(q, 'M', MapToUser(modulation, ModulationValues));
+ ST(" S ") q += PrintParameter(q, 'O', MapToUser(rollOff, RollOffValues));
+- ST(" T") q += PrintParameter(q, 'P', MapToUser(priority, PriorityValues));
+ ST(" S ") q += PrintParameter(q, 'S', MapToUser(system, SystemValues));
+ ST(" T") q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues));
+ ST(" T") q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues));
+@@ -760,6 +753,13 @@ static const char *ParseParameter(const
+ return NULL;
+ }
+
++static const char *SkipDigits(const char *s)
++{
++ while (*++s && isdigit(*s))
++ ;
++ return s;
++}
++
+ bool cChannel::StringToParameters(const char *s)
+ {
+ #ifdef USE_PLUGINPARAM
+@@ -768,7 +768,7 @@ bool cChannel::StringToParameters(const
+ while (s && *s) {
+ #endif /* PLUGINPARAM */
+ switch (toupper(*s)) {
+- case 'A': s = ParseParameter(s, alpha, AlphaValues); break;
++ case 'A': s = SkipDigits(s); break; // for compatibility with the "multiproto" approach - may be removed in future versions
+ case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break;
+ case 'C': s = ParseParameter(s, coderateH, CoderateValues); break;
+ case 'D': s = ParseParameter(s, coderateL, CoderateValues); break;
+@@ -777,14 +777,14 @@ bool cChannel::StringToParameters(const
+ case 'I': s = ParseParameter(s, inversion, InversionValues); break;
+ case 'L': polarization = *s++; break;
+ case 'M': s = ParseParameter(s, modulation, ModulationValues); break;
+- case 'Z':// for compatibility with the original DVB-S2 patch - may be removed in future versions
+ case 'O': s = ParseParameter(s, rollOff, RollOffValues); break;
+- case 'P': s = ParseParameter(s, priority, PriorityValues); break;
++ case 'P': s = SkipDigits(s); break; // for compatibility with the "multiproto" approach - may be removed in future versions
+ case 'R': polarization = *s++; break;
+ case 'S': s = ParseParameter(s, system, SystemValues); break;
+ case 'T': s = ParseParameter(s, transmission, TransmissionValues); break;
+ case 'V': polarization = *s++; break;
+ case 'Y': s = ParseParameter(s, hierarchy, HierarchyValues); break;
++ case 'Z': s = SkipDigits(s); break; // for compatibility with the original DVB-S2 patch - may be removed in future versions
+ default: esyslog("ERROR: unknown parameter key '%c'", *s);
+ return false;
+ }
+@@ -816,6 +816,8 @@ cString cChannel::ToText(const cChannel
+ q += snprintf(q, sizeof(vpidbuf), "%d", Channel->vpid);
+ if (Channel->ppid && Channel->ppid != Channel->vpid)
+ q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "+%d", Channel->ppid);
++ if (Channel->vpid && Channel->vtype)
++ q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "=%d", Channel->vtype);
+ *q = 0;
+ const int BufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod', +10: paranoia
+ char apidbuf[BufferSize];
+@@ -877,6 +879,7 @@ bool cChannel::Parse(const char *s)
+ tpid = 0;
+ }
+ vpid = ppid = 0;
++ vtype = 2; // default is MPEG-2
+ apids[0] = 0;
+ dpids[0] = 0;
+ ok = false;
+@@ -887,16 +890,20 @@ bool cChannel::Parse(const char *s)
+ ok = StringToParameters(parambuf) && (source = cSource::FromString(sourcebuf)) >= 0;
+ #endif /* PLUGINPARAM */
+
+- char *p = strchr(vpidbuf, '+');
+- if (p)
++ char *p;
++ if ((p = strchr(vpidbuf, '=')) != NULL) {
++ *p++ = 0;
++ if (sscanf(p, "%d", &vtype) != 1)
++ return false;
++ }
++ if ((p = strchr(vpidbuf, '+')) != NULL) {
+ *p++ = 0;
+- if (sscanf(vpidbuf, "%d", &vpid) != 1)
+- return false;
+- if (p) {
+ if (sscanf(p, "%d", &ppid) != 1)
+ return false;
+ }
+- else
++ if (sscanf(vpidbuf, "%d", &vpid) != 1)
++ return false;
++ if (!ppid)
+ ppid = vpid;
+
+ char *dpidbuf = strchr(apidbuf, ';');
+@@ -1057,14 +1064,72 @@ bool cChannels::Load(const char *FileNam
+ return false;
+ }
+
++void cChannels::ClearChannelHashes(void)
++{
++ channelsHashSid.Clear();
++ channelsHashNidTid.Clear();
++}
++
+ void cChannels::HashChannel(cChannel *Channel)
+ {
+ channelsHashSid.Add(Channel, Channel->Sid());
++ channelsHashNidTid.Add(Channel, HashKeyNidTid(Channel->Nid(), Channel->Tid()));
+ }
+
+ void cChannels::UnhashChannel(cChannel *Channel)
+ {
+ channelsHashSid.Del(Channel, Channel->Sid());
++ channelsHashNidTid.Del(Channel, HashKeyNidTid(Channel->Nid(), Channel->Tid()));
++}
++
++unsigned int cChannels::HashKeyNidTid(unsigned short Nid, unsigned short Tid)
++{
++ return Nid << 16 | Tid;
++}
++
++cIterator<cChannel> cChannels::GetChannelsBySourceNidTid(int Source, unsigned short Nid, unsigned short Tid)
++{
++ class cIteratorImplSourceNidTid : public cIteratorImpl {
++ private:
++ cList<cHashObject> *hashList;
++ cHashObject *current;
++ int source;
++ unsigned short nid;
++ unsigned short tid;
++ cChannel *FindMatchingChannel(bool reverse, bool reset = false) {
++ if (!hashList || (!current && !reset))
++ return NULL;
++ while (true) {
++ if (reset) {
++ reset = false;
++ current = reverse ? hashList->Last() : hashList->First();
++ }
++ else
++ current = reverse ? hashList->Prev(current) : hashList->Next(current);
++ if (!current)
++ break;
++ cChannel *Channel = (cChannel *)current->Object();
++ if (Channel->Source() == source && Channel->Nid() == nid && Channel->Tid() == tid)
++ return Channel;
++ }
++ return NULL;
++ }
++ public:
++ cIteratorImplSourceNidTid(cList<cHashObject> *HashList, int Source, unsigned short Nid, unsigned short Tid) {
++ hashList = HashList;
++ source = Source;
++ nid = Nid;
++ tid = Tid;
++ current = NULL;
++ }
++ virtual void *First(void) { return FindMatchingChannel(false, true); }
++ virtual void *Last(void) { return FindMatchingChannel(true, true); }
++ virtual void *Prev(void) { return FindMatchingChannel(true); }
++ virtual void *Next(void) { return FindMatchingChannel(false); }
++ virtual void *Current(void) const { return current ? (cChannel *)current->Object() : NULL; }
++ };
++
++ return cIterator<cChannel>(new cIteratorImplSourceNidTid(channelsHashNidTid.GetList(HashKeyNidTid(Nid, Tid)), Source, Nid, Tid));
+ }
+
+ int cChannels::GetNextGroup(int Idx)
+@@ -1101,7 +1166,7 @@ int cChannels::GetPrevNormal(int Idx)
+
+ void cChannels::ReNumber(void)
+ {
+- channelsHashSid.Clear();
++ ClearChannelHashes();
+ maxNumber = 0;
+ int Number = 1;
+ for (cChannel *channel = First(); channel; channel = Next(channel)) {
+diff -ruNp vdr-1.7.0-extensions/channels.h vdr-1.7.0-ext-h264-s2ng-speedup/channels.h
+--- vdr-1.7.0-extensions/channels.h 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/channels.h 2009-04-12 23:52:16.000000000 +0200
+@@ -4,7 +4,7 @@
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+- * $Id: channels.h 2.2 2008/04/12 13:46:50 kls Exp $
++ * $Id: channels.h 2.4 2008/11/22 13:35:52 kls Exp $
+ */
+
+ #ifndef __CHANNELS_H
+@@ -66,8 +66,6 @@ extern const tChannelParameterMap System
+ extern const tChannelParameterMap TransmissionValues[];
+ extern const tChannelParameterMap GuardValues[];
+ extern const tChannelParameterMap HierarchyValues[];
+-extern const tChannelParameterMap AlphaValues[];
+-extern const tChannelParameterMap PriorityValues[];
+ extern const tChannelParameterMap RollOffValues[];
+
+ struct tChannelID {
+@@ -127,6 +125,7 @@ private:
+ int srate;
+ int vpid;
+ int ppid;
++ int vtype;
+ int apids[MAXAPIDS + 1]; // list is zero-terminated
+ char alangs[MAXAPIDS][MAXLANGCODE2];
+ int dpids[MAXDPIDS + 1]; // list is zero-terminated
+@@ -151,17 +150,18 @@ private:
+ int transmission;
+ int guard;
+ int hierarchy;
+- int alpha;
+- int priority;
+ int rollOff;
+ int __EndData__;
+ int modification;
+ mutable const cSchedule *schedule;
+ cLinkChannels *linkChannels;
+- cChannel *refChannel;
++ cLinkChannels *refChannels;
+ cString TransponderDataToString(void) const;
+ cString ParametersToString(void) const;
+ bool StringToParameters(const char *s);
++ void AddRefChannel(cChannel *RefChannel);
++ void DelRefChannel(cChannel *RefChannel);
++ void DelLinkChannel(cChannel *RefChannel);
+ public:
+ cChannel(void);
+ cChannel(const cChannel &Channel);
+@@ -184,6 +184,7 @@ public:
+ int Srate(void) const { return srate; }
+ int Vpid(void) const { return vpid; }
+ int Ppid(void) const { return ppid; }
++ int Vtype(void) const { return vtype; }
+ const int *Apids(void) const { return apids; }
+ const int *Dpids(void) const { return dpids; }
+ const int *Spids(void) const { return spids; }
+@@ -213,11 +214,10 @@ public:
+ int Transmission(void) const { return transmission; }
+ int Guard(void) const { return guard; }
+ int Hierarchy(void) const { return hierarchy; }
+- int Alpha(void) const { return alpha; }
+- int Priority(void) const { return priority; }
+ int RollOff(void) const { return rollOff; }
+ const cLinkChannels* LinkChannels(void) const { return linkChannels; }
+- const cChannel *RefChannel(void) const { return refChannel; }
++ const cChannel *RefChannel(void) const { return refChannels ? refChannels->Last()->Channel() : 0; }
++ const cLinkChannels* RefChannels(void) const { return refChannels; }
+ #ifdef USE_PLUGINPARAM
+ bool IsPlug(void) const { return cSource::IsPlug(source); }
+ #endif /* PLUGINPARAM */
+@@ -233,18 +233,50 @@ public:
+ #endif /* PLUGINPARAM */
+ bool SetSatTransponderData(int Source, int Frequency, char Polarization, int Srate, int CoderateH, int Modulation, int System, int RollOff);
+ 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, int Alpha, int Priority);
++ 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 SetPids(int Vpid, int Ppid, int Vtype, 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);
+ void SetLinkChannels(cLinkChannels *LinkChannels);
+- void SetRefChannel(cChannel *RefChannel);
++ };
++
++class cIteratorImpl {
++private:
++ int refCount;
++ cIteratorImpl(const cIteratorImpl &);
++ const cIteratorImpl &operator =(const cIteratorImpl &);
++public:
++ cIteratorImpl(void) { refCount = 0; }
++ virtual ~cIteratorImpl() {}
++ virtual int AddRef(void) { return ++refCount; }
++ virtual int DelRef(void) { int RefCount = --refCount; if (RefCount <= 0) delete this; return RefCount; }
++ virtual void *First(void) = 0;
++ virtual void *Last(void) = 0;
++ virtual void *Prev(void) = 0;
++ virtual void *Next(void) = 0;
++ virtual void *Current(void) const = 0;
++ };
++
++template <class T> class cIterator
++{
++private:
++ cIteratorImpl *impl;
++public:
++ cIterator(cIteratorImpl *Impl) { impl = Impl; impl->AddRef(); }
++ cIterator(const cIterator &rhs) { impl = rhs.impl; impl->AddRef(); }
++ ~cIterator() { impl->DelRef(); }
++ const cIterator &operator =(const cIterator &rhs) { rhs.impl->AddRef(); impl->DelRef(); impl = rhs.impl; return *this; }
++ T *First(void) const { return (T *)impl->First(); }
++ T *Last(void) const { return (T *)impl->Last(); }
++ T *Prev(void) const { return (T *)impl->Prev(); }
++ T *Next(void) const { return (T *)impl->Next(); }
++ T *Current(void) const { return (T *)impl->Current(); }
+ };
+
+ class cChannels : public cRwLock, public cConfig<cChannel> {
+@@ -253,7 +285,10 @@ private:
+ int modified;
+ int beingEdited;
+ cHash<cChannel> channelsHashSid;
++ cHash<cChannel> channelsHashNidTid;
+ void DeleteDuplicateChannels(void);
++ void ClearChannelHashes(void);
++ static unsigned int HashKeyNidTid(unsigned short Nid, unsigned short Tid);
+ public:
+ cChannels(void);
+ bool Load(const char *FileName, bool AllowComments = false, bool MustExist = false);
+@@ -268,6 +303,7 @@ public:
+ cChannel *GetByServiceID(int Source, int Transponder, unsigned short ServiceID);
+ cChannel *GetByChannelID(tChannelID ChannelID, bool TryWithoutRid = false, bool TryWithoutPolarization = false);
+ cChannel *GetByTransponderID(tChannelID ChannelID);
++ cIterator<cChannel> GetChannelsBySourceNidTid(int Source, unsigned short Nid, unsigned short Tid);
+ int BeingEdited(void) { return beingEdited; }
+ void IncBeingEdited(void) { beingEdited++; }
+ void DecBeingEdited(void) { beingEdited--; }
+diff -ruNp vdr-1.7.0-extensions/config.c vdr-1.7.0-ext-h264-s2ng-speedup/config.c
+--- vdr-1.7.0-extensions/config.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/config.c 2009-04-12 23:57:23.000000000 +0200
+@@ -326,9 +326,6 @@ cSetup::cSetup(void)
+ ChannelBlockerMode = 0;
+ ChannelBlockerList = strdup("");
+ #endif /* DVBSETUP */
+-#ifdef USE_SYNCEARLY
+- UseSyncEarlyPatch = 0;
+-#endif /* SYNCEARLY */
+ ChannelInfoPos = 0;
+ ChannelInfoTime = 5;
+ OSDLeft = 54;
+@@ -393,9 +390,12 @@ cSetup::cSetup(void)
+ noEPGList = strdup("");
+ #endif /* NOEPG */
+ #ifdef USE_LIRCSETTINGS
++ LircPushFreq = 3;
+ LircRepeatDelay = 350;
+- LircRepeatFreq = 100;
++ LircRepeatFreq = 10;
+ LircRepeatTimeout = 500;
++ LircReconnectDelay = 3000;
++ LircPriorityBoost = 0;
+ #endif /* LIRCSETTINGS */
+ #ifdef USE_LIEMIEXT
+ ShowRecDate = 1;
+@@ -684,9 +684,6 @@ bool cSetup::Parse(const char *Name, con
+ 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);
+@@ -753,9 +750,12 @@ bool cSetup::Parse(const char *Name, con
+ }
+ #endif /* NOEPG */
+ #ifdef USE_LIRCSETTINGS
++ else if (!strcasecmp(Name, "LircPushFreq")) LircPushFreq = atoi(Value);
+ else if (!strcasecmp(Name, "LircRepeatDelay")) LircRepeatDelay = atoi(Value);
+ else if (!strcasecmp(Name, "LircRepeatFreq")) LircRepeatFreq = atoi(Value);
+ else if (!strcasecmp(Name, "LircRepeatTimeout")) LircRepeatTimeout = atoi(Value);
++ else if (!strcasecmp(Name, "LircReconnectDelay")) LircReconnectDelay = atoi(Value);
++ else if (!strcasecmp(Name, "LircPriorityBoost")) LircPriorityBoost = atoi(Value);
+ #endif /* LIRCSETTINGS */
+ #ifdef USE_LIEMIEXT
+ else if (!strcasecmp(Name, "ShowRecDate")) ShowRecDate = atoi(Value);
+@@ -916,9 +916,6 @@ bool cSetup::Save(void)
+ 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);
+@@ -982,9 +979,12 @@ bool cSetup::Save(void)
+ Store("noEPGList", noEPGList);
+ #endif /* NOEPG */
+ #ifdef USE_LIRCSETTINGS
++ Store("LircPushFreq", LircPushFreq);
+ Store("LircRepeatDelay", LircRepeatDelay);
+ Store("LircRepeatFreq", LircRepeatFreq);
+ Store("LircRepeatTimeout", LircRepeatTimeout);
++ Store("LircReconnectDelay", LircReconnectDelay);
++ Store("LircPriorityBoost", LircPriorityBoost);
+ #endif /* LIRCSETTINGS */
+ #ifdef USE_LIEMIEXT
+ Store("ShowRecDate", ShowRecDate);
+diff -ruNp vdr-1.7.0-extensions/config.h vdr-1.7.0-ext-h264-s2ng-speedup/config.h
+--- vdr-1.7.0-extensions/config.h 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/config.h 2009-04-12 23:57:23.000000000 +0200
+@@ -336,9 +336,6 @@ public:
+ int ChannelBlocker;
+ int ChannelBlockerMode;
+ #endif /* DVBSETUP */
+-#ifdef USE_SYNCEARLY
+- int UseSyncEarlyPatch;
+-#endif /* SYNCEARLY */
+ int ChannelInfoPos;
+ int ChannelInfoTime;
+ int OSDLeft, OSDTop, OSDWidth, OSDHeight;
+@@ -398,9 +395,12 @@ public:
+ int noEPGMode;
+ #endif /* NOEPG */
+ #ifdef USE_LIRCSETTINGS
++ int LircPushFreq;
+ int LircRepeatDelay;
+ int LircRepeatFreq;
+ int LircRepeatTimeout;
++ int LircReconnectDelay;
++ int LircPriorityBoost;
+ #endif /* LIRCSETTINGS */
+ #ifdef USE_LIEMIEXT
+ int ShowRecDate, ShowRecTime, ShowRecLength, ShowProgressBar, MenuCmdPosition;
+diff -ruNp vdr-1.7.0-extensions/device.c vdr-1.7.0-ext-h264-s2ng-speedup/device.c
+--- vdr-1.7.0-extensions/device.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/device.c 2009-04-12 23:57:23.000000000 +0200
+@@ -1110,11 +1110,7 @@ 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 */
++ if (!NeedsTransferMode || GetCurrentAudioTrack() == ttNone)
+ EnsureAudioTrack(true);
+ EnsureSubtitleTrack();
+ }
+diff -ruNp vdr-1.7.0-extensions/dvbdevice.c vdr-1.7.0-ext-h264-s2ng-speedup/dvbdevice.c
+--- vdr-1.7.0-extensions/dvbdevice.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/dvbdevice.c 2009-04-13 12:28:52.000000000 +0200
+@@ -4,13 +4,13 @@
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+- * $Id: dvbdevice.c 2.2 2008/04/13 14:15:35 kls Exp $
++ * $Id: dvbdevice.c 2.14 2009/04/10 09:54:24 kls Exp $
+ */
+
+ #include "dvbdevice.h"
+ #include <errno.h>
+ #include <limits.h>
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
+ #include <linux/dvb/audio.h>
+ #include <linux/dvb/dmx.h>
+ #include <linux/dvb/frontend.h>
+@@ -79,7 +79,7 @@ private:
+ int tuneTimeout;
+ int lockTimeout;
+ time_t lastTimeoutReport;
+- dvbfe_delsys frontendType;
++ fe_delivery_system frontendType;
+ cChannel channel;
+ const char *diseqcCommands;
+ eTunerStatus tunerStatus;
+@@ -93,7 +93,7 @@ private:
+ bool SetFrontend(void);
+ virtual void Action(void);
+ public:
+- cDvbTuner(int Fd_Frontend, int CardIndex, dvbfe_delsys FrontendType);
++ cDvbTuner(int Fd_Frontend, int CardIndex, fe_delivery_system FrontendType);
+ virtual ~cDvbTuner();
+ bool IsTunedTo(const cChannel *Channel) const;
+ void Set(const cChannel *Channel, bool Tune);
+@@ -103,7 +103,7 @@ public:
+ bool Locked(int TimeoutMs = 0);
+ };
+
+-cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, dvbfe_delsys FrontendType)
++cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_delivery_system FrontendType)
+ {
+ fd_frontend = Fd_Frontend;
+ #ifdef USE_ROTOR
+@@ -116,7 +116,7 @@ cDvbTuner::cDvbTuner(int Fd_Frontend, in
+ lastTimeoutReport = 0;
+ diseqcCommands = NULL;
+ tunerStatus = tsIdle;
+- if (frontendType & (DVBFE_DELSYS_DVBS | DVBFE_DELSYS_DVBS2))
++ if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)
+ CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
+ SetDescription("tuner on device %d", cardIndex + 1);
+ Start();
+@@ -139,7 +139,6 @@ bool cDvbTuner::IsTunedTo(const cChannel
+ char Type = **cSource::ToString(Channel->Source());
+ #define ST(s, p) if (strchr(s, Type)) if (channel.p() != Channel->p()) return false;
+ // Polarization is already checked as part of the Transponder.
+- ST(" T", Alpha);
+ ST(" T", Bandwidth);
+ ST("CST", CoderateH);
+ ST(" T", CoderateL);
+@@ -147,8 +146,8 @@ bool cDvbTuner::IsTunedTo(const cChannel
+ ST("CST", Inversion);
+ ST("CST", Modulation);
+ ST(" S ", RollOff);
+- ST(" T", Priority);
+ ST(" S ", System);
++ ST("CS ", Srate);
+ ST(" T", Transmission);
+ ST(" T", Hierarchy);
+ return true;
+@@ -180,7 +179,7 @@ bool cDvbTuner::Locked(int TimeoutMs)
+ bool cDvbTuner::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
+ {
+ cMutexLock MutexLock(&mutex);
+- if ((!DVBFE_DELSYS_DVBS & !DVBFE_DELSYS_DVBS2) || SendDiseqc)
++ if ((!SYS_DVBS & !SYS_DVBS2) || SendDiseqc)
+ return false;
+ diseqc_cmd = cmd;
+ SendDiseqc = true;
+@@ -217,10 +216,27 @@ static unsigned int FrequencyToHz(unsign
+
+ bool cDvbTuner::SetFrontend(void)
+ {
+- dvbfe_params Frontend;
++#define MAXFRONTENDCMDS 16
++#define SETCMD(c, d) { Frontend[CmdSeq.num].cmd = (c);\
++ Frontend[CmdSeq.num].u.data = (d);\
++ if (CmdSeq.num++ > MAXFRONTENDCMDS) {\
++ esyslog("ERROR: too many tuning commands on frontend %d", cardIndex);\
++ return false;\
++ }\
++ }
++ dtv_property Frontend[MAXFRONTENDCMDS];
+ memset(&Frontend, 0, sizeof(Frontend));
++ dtv_properties CmdSeq;
++ memset(&CmdSeq, 0, sizeof(CmdSeq));
++ CmdSeq.props = Frontend;
++ SETCMD(DTV_CLEAR, 0);
++ if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
++ esyslog("ERROR: frontend %d: %m", cardIndex);
++ return false;
++ }
++ CmdSeq.num = 0;
+
+- if (frontendType & (DVBFE_DELSYS_DVBS | DVBFE_DELSYS_DVBS2)) {
++ if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2) {
+ unsigned int frequency = channel.Frequency();
+ if (Setup.DiSEqC) {
+ cDiseqc *diseqc = Diseqcs.Get(channel.Source(), channel.Frequency(), channel.Polarization());
+@@ -277,85 +293,78 @@ bool cDvbTuner::SetFrontend(void)
+ }
+ frequency = abs(frequency); // Allow for C-band, where the frequency is less than the LOF
+
+- Frontend.delivery = dvbfe_delsys(channel.System());
+- Frontend.frequency = frequency * 1000UL;
+- Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
+- if (Frontend.delivery == DVBFE_DELSYS_DVBS) {
+- Frontend.delsys.dvbs.modulation = dvbfe_modulation(channel.Modulation());
+- Frontend.delsys.dvbs.symbol_rate = channel.Srate() * 1000UL;
+- Frontend.delsys.dvbs.fec = dvbfe_fec(channel.CoderateH());
++ // DVB-S/DVB-S2 (common parts)
++ SETCMD(DTV_DELIVERY_SYSTEM, channel.System());
++ SETCMD(DTV_FREQUENCY, frequency * 1000UL);
++ SETCMD(DTV_MODULATION, channel.Modulation());
++ SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL);
++ SETCMD(DTV_INNER_FEC, channel.CoderateH());
++ SETCMD(DTV_INVERSION, channel.Inversion());
++ if (channel.System() == SYS_DVBS2) {
++ if (frontendType == SYS_DVBS2) {
++ // DVB-S2
++ SETCMD(DTV_PILOT, PILOT_AUTO);
++ SETCMD(DTV_ROLLOFF, channel.RollOff());
++ }
++ else {
++ esyslog("ERROR: frontend %d doesn't provide DVB-S2", cardIndex);
++ return false;
++ }
+ }
+ else {
+- Frontend.delsys.dvbs2.modulation = dvbfe_modulation(channel.Modulation());
+- Frontend.delsys.dvbs2.symbol_rate = channel.Srate() * 1000UL;
+- Frontend.delsys.dvbs2.fec = dvbfe_fec(channel.CoderateH());
+- Frontend.delsys.dvbs2.rolloff = dvbfe_rolloff(channel.RollOff());
++ // DVB-S
++ SETCMD(DTV_ROLLOFF, ROLLOFF_35); // DVB-S always has a ROLLOFF of 0.35
+ }
+
+ tuneTimeout = DVBS_TUNE_TIMEOUT;
+ lockTimeout = DVBS_LOCK_TIMEOUT;
+-
+- dvbfe_info feinfo;
+- feinfo.delivery = Frontend.delivery;
+- CHECK(ioctl(fd_frontend, DVBFE_GET_INFO, &feinfo)); //switch system
+- }
+- else if (frontendType & DVBFE_DELSYS_DVBC) {
+- Frontend.delivery = DVBFE_DELSYS_DVBC;
+- Frontend.frequency = FrequencyToHz(channel.Frequency());
+- Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
+- Frontend.delsys.dvbc.symbol_rate = channel.Srate() * 1000UL;
+- Frontend.delsys.dvbc.fec = dvbfe_fec(channel.CoderateH());
+- Frontend.delsys.dvbc.modulation = dvbfe_modulation(channel.Modulation());
++ }
++ else if (frontendType == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B) {
++ // DVB-C
++ SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
++ SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
++ SETCMD(DTV_INVERSION, channel.Inversion());
++ SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL);
++ SETCMD(DTV_INNER_FEC, channel.CoderateH());
++ SETCMD(DTV_MODULATION, channel.Modulation());
+
+ tuneTimeout = DVBC_TUNE_TIMEOUT;
+ lockTimeout = DVBC_LOCK_TIMEOUT;
+-
+- dvbfe_info feinfo;
+- feinfo.delivery = Frontend.delivery;
+- CHECK(ioctl(fd_frontend, DVBFE_GET_INFO, &feinfo)); //switch system
+ }
+ #ifdef USE_ATSC
+- else if (frontendType & DVBFE_DELSYS_ATSC) {
+- // Frequency and symbol rate:
++ else if (frontendType == SYS_ATSC) {
++ // ATSC
++ SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
++ SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
++ SETCMD(DTV_INVERSION, channel.Inversion());
++ SETCMD(DTV_MODULATION, channel.Modulation());
+
+- Frontend.frequency = FrequencyToHz(channel.Frequency());
+- Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
+- Frontend.delsys.atsc.modulation = dvbfe_modulation(channel.Modulation());
+-
+- tuneTimeout = DVBC_TUNE_TIMEOUT;
+- lockTimeout = DVBC_LOCK_TIMEOUT;
+-
+- dvbfe_info feinfo;
+- feinfo.delivery = Frontend.delivery;
+- CHECK(ioctl(fd_frontend, DVBFE_GET_INFO, &feinfo)); //switch system
++ tuneTimeout = DVBT_TUNE_TIMEOUT;
++ lockTimeout = DVBT_LOCK_TIMEOUT;
+ }
+ #endif /* ATSC */
+- else if (frontendType & DVBFE_DELSYS_DVBT) {
+- Frontend.delivery = DVBFE_DELSYS_DVBT;
+- Frontend.frequency = FrequencyToHz(channel.Frequency());
+- Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
+- Frontend.delsys.dvbt.bandwidth = dvbfe_bandwidth(channel.Bandwidth());
+- Frontend.delsys.dvbt.code_rate_HP = dvbfe_fec(channel.CoderateH());
+- Frontend.delsys.dvbt.code_rate_LP = dvbfe_fec(channel.CoderateL());
+- Frontend.delsys.dvbt.constellation = dvbfe_modulation(channel.Modulation());
+- Frontend.delsys.dvbt.transmission_mode = dvbfe_transmission_mode(channel.Transmission());
+- Frontend.delsys.dvbt.guard_interval = dvbfe_guard_interval(channel.Guard());
+- Frontend.delsys.dvbt.hierarchy = dvbfe_hierarchy(channel.Hierarchy());
+- Frontend.delsys.dvbt.alpha = dvbfe_alpha(channel.Alpha());
+- Frontend.delsys.dvbt.priority = dvbfe_stream_priority(channel.Priority());
++ else if (frontendType == SYS_DVBT) {
++ // DVB-T
++ SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
++ SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
++ SETCMD(DTV_INVERSION, channel.Inversion());
++ SETCMD(DTV_BANDWIDTH_HZ, channel.Bandwidth());
++ SETCMD(DTV_CODE_RATE_HP, channel.CoderateH());
++ SETCMD(DTV_CODE_RATE_LP, channel.CoderateL());
++ SETCMD(DTV_MODULATION, channel.Modulation());
++ SETCMD(DTV_TRANSMISSION_MODE, channel.Transmission());
++ SETCMD(DTV_GUARD_INTERVAL, channel.Guard());
++ SETCMD(DTV_HIERARCHY, channel.Hierarchy());
+
+ tuneTimeout = DVBT_TUNE_TIMEOUT;
+ lockTimeout = DVBT_LOCK_TIMEOUT;
+-
+- dvbfe_info feinfo;
+- feinfo.delivery = Frontend.delivery;
+- CHECK(ioctl(fd_frontend, DVBFE_GET_INFO, &feinfo)); //switch system
+ }
+ else {
+ esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
+ return false;
+ }
+- if (ioctl(fd_frontend, DVBFE_SET_PARAMS, &Frontend) < 0) {
++ SETCMD(DTV_TUNE, 0);
++ if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
+ esyslog("ERROR: frontend %d: %m", cardIndex);
+ return false;
+ }
+@@ -433,13 +442,22 @@ int cDvbDevice::devVideoOffset = -1;
+ int cDvbDevice::setTransferModeForDolbyDigital = 1;
+
+ const char *DeliverySystems[] = {
+- "DVBS",
++ "UNDEFINED",
++ "DVB-C",
++ "DVB-C",
++ "DVB-T",
+ "DSS",
+- "DVBS2",
+- "DVBC",
+- "DVBT",
+- "DVBH",
++ "DVB-S",
++ "DVB-S2",
++ "DVB-H",
++ "ISDBT",
++ "ISDBS",
++ "ISDBC",
+ "ATSC",
++ "ATSCMH",
++ "DMBTH",
++ "CMMB",
++ "DAB",
+ NULL
+ };
+
+@@ -447,7 +465,7 @@ cDvbDevice::cDvbDevice(int n)
+ {
+ ciAdapter = NULL;
+ dvbTuner = NULL;
+- frontendType = DVBFE_DELSYS_DUMMY;
++ frontendType = SYS_UNDEFINED;
+ numProvidedSystems = 0;
+ spuDecoder = NULL;
+ digitalAudio = false;
+@@ -510,30 +528,29 @@ cDvbDevice::cDvbDevice(int n)
+ // We only check the devices that must be present - the others will be checked before accessing them://XXX
+
+ if (fd_frontend >= 0) {
+- if (ioctl(fd_frontend, DVBFE_GET_DELSYS, &frontendType) >= 0) {
+- const char **DeliverySystem = DeliverySystems;
+- cString ds;
++ if (ioctl(fd_frontend, FE_GET_INFO, &frontendInfo) >= 0) {
+ #ifdef USE_DVBSETUP
+ if (Setup.ChannelBlockerMode == 4)
+- frontendType = n == Setup.PrimaryDVB - 1 ? DVBFE_DELSYS_DUMMY : frontendType;
++ frontendType = (n == Setup.PrimaryDVB - 1) ? SYS_UNDEFINED : frontendType;
++ else
+ #endif /* DVBSETUP */
+- for (int i = 0; i < 32; i++) {
+- if (frontendType & (1u << i)) {
+- numProvidedSystems++;
+- if (*DeliverySystem)
+- ds = cString::sprintf("%s %s", *ds ? *ds : "", *DeliverySystem);
+- else
+- esyslog("ERROR: unknown delivery system %d", i);
+- }
+- if (*DeliverySystem)
+- DeliverySystem++;
+- }
+- if (*ds)
+- isyslog("device %d provides:%s", CardIndex() + 1, *ds);
+- dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType);
++ switch (frontendInfo.type) {
++ case FE_QPSK: frontendType = (frontendInfo.caps & FE_CAN_2G_MODULATION) ? SYS_DVBS2 : SYS_DVBS; break;
++ case FE_OFDM: frontendType = SYS_DVBT; break;
++ case FE_QAM: frontendType = SYS_DVBC_ANNEX_AC; break;
++ case FE_ATSC: frontendType = SYS_ATSC; break;
++ default: esyslog("ERROR: unknown frontend type %d on device %d", frontendInfo.type, CardIndex() + 1);
++ }
+ }
+ else
+ LOG_ERROR;
++ if (frontendType != SYS_UNDEFINED) {
++ numProvidedSystems++;
++ if (frontendType == SYS_DVBS2)
++ numProvidedSystems++;
++ isyslog("device %d provides %s (\"%s\")", CardIndex() + 1, DeliverySystems[frontendType], frontendInfo.name);
++ dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType);
++ }
+ }
+ else
+ esyslog("ERROR: can't open DVB device %d", n);
+@@ -631,69 +648,103 @@ uchar *cDvbDevice::GrabImage(int &Size,
+ int videoDev = open(buffer, O_RDWR);
+ if (videoDev >= 0) {
+ uchar *result = NULL;
+- struct video_mbuf mbuf;
+- if (ioctl(videoDev, VIDIOCGMBUF, &mbuf) == 0) {
+- int msize = mbuf.size;
+- unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0);
+- if (mem && mem != (unsigned char *)-1) {
+- // set up the size and RGB
+- struct video_capability vc;
+- if (ioctl(videoDev, VIDIOCGCAP, &vc) == 0) {
+- struct video_mmap vm;
+- vm.frame = 0;
+- if ((SizeX > 0) && (SizeX <= vc.maxwidth) &&
+- (SizeY > 0) && (SizeY <= vc.maxheight)) {
+- vm.width = SizeX;
+- vm.height = SizeY;
+- }
+- else {
+- vm.width = vc.maxwidth;
+- vm.height = vc.maxheight;
+- }
+- vm.format = VIDEO_PALETTE_RGB24;
+- if (ioctl(videoDev, VIDIOCMCAPTURE, &vm) == 0 && ioctl(videoDev, VIDIOCSYNC, &vm.frame) == 0) {
+- // make RGB out of BGR:
+- int memsize = vm.width * vm.height;
+- unsigned char *mem1 = mem;
+- for (int i = 0; i < memsize; i++) {
+- unsigned char tmp = mem1[2];
+- mem1[2] = mem1[0];
+- mem1[0] = tmp;
+- mem1 += 3;
+- }
+-
+- if (Quality < 0)
+- Quality = 100;
+-
+- dsyslog("grabbing to %s %d %d %d", Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height);
+- if (Jpeg) {
+- // convert to JPEG:
+- result = RgbToJpeg(mem, vm.width, vm.height, Size, Quality);
+- if (!result)
+- esyslog("ERROR: failed to convert image to JPEG");
+- }
+- else {
+- // convert to PNM:
+- char buf[32];
+- snprintf(buf, sizeof(buf), "P6\n%d\n%d\n255\n", vm.width, vm.height);
+- int l = strlen(buf);
+- int bytes = memsize * 3;
+- Size = l + bytes;
+- result = MALLOC(uchar, Size);
+- if (result) {
+- memcpy(result, buf, l);
+- memcpy(result + l, mem, bytes);
++ // set up the size and RGB
++ v4l2_format fmt;
++ memset(&fmt, 0, sizeof(fmt));
++ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ fmt.fmt.pix.width = SizeX;
++ fmt.fmt.pix.height = SizeY;
++ fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
++ fmt.fmt.pix.field = V4L2_FIELD_ANY;
++ if (ioctl(videoDev, VIDIOC_S_FMT, &fmt) == 0) {
++ v4l2_requestbuffers reqBuf;
++ memset(&reqBuf, 0, sizeof(reqBuf));
++ reqBuf.count = 2;
++ reqBuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ reqBuf.memory = V4L2_MEMORY_MMAP;
++ if (ioctl(videoDev, VIDIOC_REQBUFS, &reqBuf) >= 0) {
++ v4l2_buffer mbuf;
++ memset(&mbuf, 0, sizeof(mbuf));
++ mbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ mbuf.memory = V4L2_MEMORY_MMAP;
++ if (ioctl(videoDev, VIDIOC_QUERYBUF, &mbuf) == 0) {
++ int msize = mbuf.length;
++ unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0);
++ if (mem && mem != (unsigned char *)-1) {
++ v4l2_buffer buf;
++ memset(&buf, 0, sizeof(buf));
++ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ buf.memory = V4L2_MEMORY_MMAP;
++ buf.index = 0;
++ if (ioctl(videoDev, VIDIOC_QBUF, &buf) == 0) {
++ v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ if (ioctl (videoDev, VIDIOC_STREAMON, &type) == 0) {
++ memset(&buf, 0, sizeof(buf));
++ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ buf.memory = V4L2_MEMORY_MMAP;
++ buf.index = 0;
++ if (ioctl(videoDev, VIDIOC_DQBUF, &buf) == 0) {
++ if (ioctl(videoDev, VIDIOC_STREAMOFF, &type) == 0) {
++ // make RGB out of BGR:
++ int memsize = fmt.fmt.pix.width * fmt.fmt.pix.height;
++ unsigned char *mem1 = mem;
++ for (int i = 0; i < memsize; i++) {
++ unsigned char tmp = mem1[2];
++ mem1[2] = mem1[0];
++ mem1[0] = tmp;
++ mem1 += 3;
++ }
++
++ if (Quality < 0)
++ Quality = 100;
++
++ dsyslog("grabbing to %s %d %d %d", Jpeg ? "JPEG" : "PNM", Quality, fmt.fmt.pix.width, fmt.fmt.pix.height);
++ if (Jpeg) {
++ // convert to JPEG:
++ result = RgbToJpeg(mem, fmt.fmt.pix.width, fmt.fmt.pix.height, Size, Quality);
++ if (!result)
++ esyslog("ERROR: failed to convert image to JPEG");
++ }
++ else {
++ // convert to PNM:
++ char buf[32];
++ snprintf(buf, sizeof(buf), "P6\n%d\n%d\n255\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
++ int l = strlen(buf);
++ int bytes = memsize * 3;
++ Size = l + bytes;
++ result = MALLOC(uchar, Size);
++ if (result) {
++ memcpy(result, buf, l);
++ memcpy(result + l, mem, bytes);
++ }
++ else
++ esyslog("ERROR: failed to convert image to PNM");
++ }
++ }
++ else
++ esyslog("ERROR: video device VIDIOC_STREAMOFF failed");
++ }
++ else
++ esyslog("ERROR: video device VIDIOC_DQBUF failed");
+ }
+ else
+- esyslog("ERROR: failed to convert image to PNM");
++ esyslog("ERROR: video device VIDIOC_STREAMON failed");
+ }
++ else
++ esyslog("ERROR: video device VIDIOC_QBUF failed");
++ munmap(mem, msize);
+ }
++ else
++ esyslog("ERROR: failed to memmap video device");
+ }
+- munmap(mem, msize);
++ else
++ esyslog("ERROR: video device VIDIOC_QUERYBUF failed");
+ }
+ else
+- esyslog("ERROR: failed to memmap video device");
++ esyslog("ERROR: video device VIDIOC_REQBUFS failed");
+ }
++ else
++ esyslog("ERROR: video device VIDIOC_S_FMT failed");
+ close(videoDev);
+ return result;
+ }
+@@ -861,7 +912,7 @@ bool cDvbDevice::ProvidesSource(int Sour
+ {
+ int type = Source & cSource::st_Mask;
+ #ifdef USE_SOURCECAPS
+- if (Setup.SourceCapsSet && type == cSource::stSat && (frontendType & (DVBFE_DELSYS_DVBS | DVBFE_DELSYS_DVBS2))) {
++ if (Setup.SourceCapsSet && type == cSource::stSat && (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)) {
+ for (int i = 0; i < MAXSOURCECAPS; i++)
+ if (sourceCaps[i] == Source)
+ return true;
+@@ -870,13 +921,13 @@ bool cDvbDevice::ProvidesSource(int Sour
+ else
+ #endif /* SOURCECAPS */
+ return type == cSource::stNone
+- || type == cSource::stCable && (frontendType & DVBFE_DELSYS_DVBC)
++ || type == cSource::stCable && (frontendType == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B)
++ || type == cSource::stSat && (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)
+ #ifdef USE_ATSC
+- || type == cSource::stCable && (frontendType & DVBFE_DELSYS_ATSC)
+- || type == cSource::stTerr && (frontendType & DVBFE_DELSYS_ATSC)
++ || type == cSource::stTerr && (frontendType == SYS_DVBT || frontendType == SYS_ATSC);
++#else
++ || type == cSource::stTerr && (frontendType == SYS_DVBT);
+ #endif /* ATSC */
+- || type == cSource::stSat && (frontendType & (DVBFE_DELSYS_DVBS | DVBFE_DELSYS_DVBS2))
+- || type == cSource::stTerr && (frontendType & DVBFE_DELSYS_DVBT);
+ }
+
+ bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const
+@@ -901,7 +952,7 @@ bool cDvbDevice::ProvidesTransponder(con
+ return false; // doesn't provide source
+ if (!cSource::IsSat(Channel->Source()))
+ return true; // source is sufficient for non sat
+- if (!(frontendType & Channel->System()))
++ if (frontendType == SYS_DVBS && Channel->System() == SYS_DVBS2)
+ return false; // requires modulation system which frontend doesn't provide
+ return !Setup.DiSEqC || Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization());
+ }
+@@ -1240,8 +1291,10 @@ void cDvbDevice::Play(void)
+ CHECK(ioctl(fd_audio, AUDIO_CONTINUE));
+ }
+ else {
+- if (fd_audio >= 0)
++ if (fd_audio >= 0) {
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
++ CHECK(ioctl(fd_audio, AUDIO_CONTINUE));
++ }
+ if (fd_video >= 0)
+ CHECK(ioctl(fd_video, VIDEO_CONTINUE));
+ }
+@@ -1255,8 +1308,10 @@ void cDvbDevice::Freeze(void)
+ CHECK(ioctl(fd_audio, AUDIO_PAUSE));
+ }
+ else {
+- if (fd_audio >= 0)
++ if (fd_audio >= 0) {
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
++ CHECK(ioctl(fd_audio, AUDIO_PAUSE));
++ }
+ if (fd_video >= 0)
+ CHECK(ioctl(fd_video, VIDEO_FREEZE));
+ }
+@@ -1274,7 +1329,13 @@ void cDvbDevice::Mute(void)
+
+ void cDvbDevice::StillPicture(const uchar *Data, int Length)
+ {
+- if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01 && (Data[3] & 0xF0) == 0xE0) {
++ if (!Data || Length < TS_SIZE)
++ return;
++ if (Data[0] == 0x47) {
++ // TS data
++ cDevice::StillPicture(Data, Length);
++ }
++ else if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01 && (Data[3] & 0xF0) == 0xE0) {
+ // PES data
+ char *buf = MALLOC(char, Length);
+ if (!buf)
+@@ -1369,6 +1430,16 @@ int cDvbDevice::PlayAudio(const uchar *D
+ return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
+ }
+
++int cDvbDevice::PlayTsVideo(const uchar *Data, int Length)
++{
++ return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
++}
++
++int cDvbDevice::PlayTsAudio(const uchar *Data, int Length)
++{
++ return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
++}
++
+ bool cDvbDevice::OpenDvr(void)
+ {
+ CloseDvr();
+diff -ruNp vdr-1.7.0-extensions/dvbdevice.h vdr-1.7.0-ext-h264-s2ng-speedup/dvbdevice.h
+--- vdr-1.7.0-extensions/dvbdevice.h 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/dvbdevice.h 2009-04-12 13:37:59.000000000 +0200
+@@ -4,7 +4,7 @@
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+- * $Id: dvbdevice.h 2.1 2008/04/12 11:20:48 kls Exp $
++ * $Id: dvbdevice.h 2.3 2008/12/06 13:31:12 kls Exp $
+ */
+
+ #ifndef __DVBDEVICE_H
+@@ -15,8 +15,8 @@
+ #include "device.h"
+ #include "dvbspu.h"
+
+-#if DVB_API_VERSION != 3 || DVB_API_VERSION_MINOR != 3
+-#error VDR requires Linux DVB driver API version 3.3!
++#if DVB_API_VERSION != 5 || DVB_API_VERSION_MINOR != 0
++#error VDR requires Linux DVB driver API version 5.0!
+ #endif
+
+ #define MAXDVBDEVICES 8
+@@ -35,8 +35,9 @@ public:
+ ///< Must be called before accessing any DVB functions.
+ ///< \return True if any devices are available.
+ private:
+- dvbfe_delsys frontendType;
++ dvb_frontend_info frontendInfo;
+ int numProvidedSystems;
++ fe_delivery_system frontendType;
+ int fd_osd, fd_audio, fd_video, fd_dvr, fd_stc, fd_ca;
+ protected:
+ virtual void MakePrimaryDevice(bool On);
+@@ -141,6 +142,8 @@ protected:
+ virtual bool SetPlayMode(ePlayMode PlayMode);
+ virtual int PlayVideo(const uchar *Data, int Length);
+ virtual int PlayAudio(const uchar *Data, int Length, uchar Id);
++ virtual int PlayTsVideo(const uchar *Data, int Length);
++ virtual int PlayTsAudio(const uchar *Data, int Length);
+ public:
+ virtual int64_t GetSTC(void);
+ virtual void TrickSpeed(int Speed);
+diff -ruNp vdr-1.7.0-extensions/dvbplayer.c vdr-1.7.0-ext-h264-s2ng-speedup/dvbplayer.c
+--- vdr-1.7.0-extensions/dvbplayer.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/dvbplayer.c 2009-04-12 23:57:23.000000000 +0200
+@@ -185,8 +185,8 @@ bool cNonBlockingFileReader::WaitForData
+
+ #define PLAYERBUFSIZE MEGABYTE(1)
+
+-// The number of frames to back up when resuming an interrupted replay session:
+-#define RESUMEBACKUP (10 * FRAMESPERSEC)
++// The number of seconds to back up when resuming an interrupted replay session:
++#define RESUMEBACKUP 10
+
+ class cDvbPlayer : public cPlayer, cThread {
+ private:
+@@ -202,6 +202,7 @@ private:
+ cFileName *fileName;
+ cIndexFile *index;
+ cUnbufferedFile *replayFile;
++ int framesPerSec;
+ bool eof;
+ bool firstPacket;
+ ePlayModes playMode;
+@@ -231,6 +232,7 @@ public:
+ void Goto(int Position, bool Still = false);
+ virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
+ virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
++ int GetFramesPerSec(void) { return framesPerSec; }
+ };
+
+ #define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
+@@ -263,6 +265,7 @@ cDvbPlayer::cDvbPlayer(const char *FileN
+ replayFile = fileName->Open();
+ if (!replayFile)
+ return;
++ framesPerSec = replayFile->GetFramesPerSec();
+ ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
+ // Create the index file:
+ index = new cIndexFile(FileName, false);
+@@ -385,7 +388,7 @@ bool cDvbPlayer::Save(void)
+ if (Setup.PlayJump && marks.First() && abs(Index - marks.First()->position) <= RESUMEBACKUP)
+ Index = 0;
+ #endif /* JUMPPLAY */
+- Index -= RESUMEBACKUP;
++ Index -= RESUMEBACKUP * GetFramesPerSec();
+ if (Index > 0)
+ Index = index->GetNextIFrame(Index, false);
+ else
+@@ -407,127 +410,6 @@ 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;
+@@ -540,7 +422,7 @@ void cDvbPlayer::Action(void)
+
+ readIndex = Resume();
+ if (readIndex >= 0)
+- isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true));
++ isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, GetFramesPerSec()));
+
+ #ifdef USE_JUMPPLAY
+ if (Setup.PlayJump && readIndex <= 0 && marks.First() && index) {
+@@ -550,7 +432,7 @@ void cDvbPlayer::Action(void)
+ if (index->Get(Index, &FileNumber, &FileOffset) &&
+ NextFile(FileNumber, FileOffset)) {
+ isyslog("PlayJump: start replay at first mark %d (%s)",
+- Index, *IndexToHMSF(Index, true));
++ Index, *IndexToHMSF(Index, true, GetFramesPerSec()));
+ readIndex = Index;
+ }
+ }
+@@ -599,9 +481,6 @@ void cDvbPlayer::Action(void)
+ if (!NextFile(FileNumber, FileOffset)) {
+ readIndex = Index;
+ continue;
+-#ifdef USE_DVBPLAYER
+- Length += IPACKS; // fixIFrame needs next video packet
+-#endif /* DVBPLAYER */
+ }
+ }
+ else {
+@@ -688,10 +567,6 @@ 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;
+ }
+@@ -933,7 +808,7 @@ void cDvbPlayer::SkipSeconds(int Seconds
+ Empty();
+ int Index = writeIndex;
+ if (Index >= 0) {
+- Index = max(Index + Seconds * FRAMESPERSEC, 0);
++ Index = max(Index + Seconds * GetFramesPerSec(), 0);
+ if (Index > 0)
+ Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true);
+ if (Index >= 0)
+@@ -954,15 +829,9 @@ 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
+@@ -986,7 +855,8 @@ void cDvbPlayer::Goto(int Index, bool St
+ b[r++] = 0x00;
+ b[r++] = 0x00;
+ b[r++] = 0x01;
+- b[r++] = 0xB7;
++ b[r] = (cRemux::IsFrameH264(b, r) ? 10 : 0xB7);
++ r++;
+ }
+ DeviceStillPicture(b, r);
+ }
+@@ -1106,3 +976,10 @@ void cDvbPlayerControl::Goto(int Positio
+ if (player)
+ player->Goto(Position, Still);
+ }
++
++int cDvbPlayerControl::GetFramesPerSec()
++{
++ if (player)
++ return player->GetFramesPerSec();
++ return FRAMESPERSEC;
++}
+diff -ruNp vdr-1.7.0-extensions/dvbplayer.h vdr-1.7.0-ext-h264-s2ng-speedup/dvbplayer.h
+--- vdr-1.7.0-extensions/dvbplayer.h 2002-06-23 12:13:51.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/dvbplayer.h 2009-04-12 23:57:23.000000000 +0200
+@@ -54,6 +54,8 @@ public:
+ void Goto(int Index, bool Still = false);
+ // Positions to the given index and displays that frame as a still picture
+ // if Still is true.
++ int GetFramesPerSec();
++ // Returns the number of frames per second for the current recording.
+ };
+
+ #endif //__DVBPLAYER_H
+diff -ruNp vdr-1.7.0-extensions/epg.c vdr-1.7.0-ext-h264-s2ng-speedup/epg.c
+--- vdr-1.7.0-extensions/epg.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/epg.c 2009-04-12 23:57:23.000000000 +0200
+@@ -1420,6 +1420,7 @@ cSchedule *cSchedules::AddSchedule(tChan
+ if (!p) {
+ p = new cSchedule(ChannelID);
+ Add(p);
++ HashSchedule(p);
+ cChannel *channel = Channels.GetByChannelID(ChannelID);
+ if (channel)
+ channel->schedule = p;
+@@ -1430,10 +1431,14 @@ cSchedule *cSchedules::AddSchedule(tChan
+ const cSchedule *cSchedules::GetSchedule(tChannelID ChannelID) const
+ {
+ ChannelID.ClrRid();
+- for (cSchedule *p = First(); p; p = Next(p)) {
+- if (p->ChannelID() == ChannelID)
+- return p;
+- }
++ cList<cHashObject> *list = schedulesHash.GetList(HashKey(ChannelID));
++ if (list) {
++ for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
++ cSchedule *p = (cSchedule *)hobj->Object();
++ if (p->ChannelID() == ChannelID)
++ return p;
++ }
++ }
+ return NULL;
+ }
+
+@@ -1449,7 +1454,23 @@ const cSchedule *cSchedules::GetSchedule
+ if (Channel->schedule == &DummySchedule && AddIfMissing) {
+ cSchedule *Schedule = new cSchedule(Channel->GetChannelID());
+ ((cSchedules *)this)->Add(Schedule);
++ ((cSchedules *)this)->HashSchedule(Schedule);
+ Channel->schedule = Schedule;
+ }
+ return Channel->schedule != &DummySchedule? Channel->schedule : NULL;
+ }
++
++void cSchedules::HashSchedule(cSchedule *Schedule)
++{
++ schedulesHash.Add(Schedule, HashKey(Schedule->ChannelID().ClrRid()));
++}
++
++void cSchedules::UnhashSchedule(cSchedule *Schedule)
++{
++ schedulesHash.Del(Schedule, HashKey(Schedule->ChannelID().ClrRid()));
++}
++
++unsigned int cSchedules::HashKey(tChannelID ChannelID)
++{
++ return (unsigned int)((ChannelID.Nid() << 16 | ChannelID.Source()) ^ (ChannelID.Tid() << 16 | ChannelID.Sid()) ^ ChannelID.Rid());
++}
+diff -ruNp vdr-1.7.0-extensions/epg.h vdr-1.7.0-ext-h264-s2ng-speedup/epg.h
+--- vdr-1.7.0-extensions/epg.h 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/epg.h 2009-04-12 23:57:23.000000000 +0200
+@@ -199,11 +199,15 @@ class cSchedules : public cList<cSchedul
+ friend class cSchedulesLock;
+ private:
+ cRwLock rwlock;
++ cHash<cSchedule> schedulesHash;
+ static cSchedules schedules;
+ static const char *epgDataFileName;
+ static time_t lastCleanup;
+ static time_t lastDump;
+ static time_t modified;
++ void HashSchedule(cSchedule *Schedule);
++ void UnhashSchedule(cSchedule *Schedule);
++ static unsigned int HashKey(tChannelID ChannelID);
+ public:
+ static void SetEpgDataFileName(const char *FileName);
+ static const cSchedules *Schedules(cSchedulesLock &SchedulesLock);
+diff -ruNp vdr-1.7.0-extensions/h264parser.c vdr-1.7.0-ext-h264-s2ng-speedup/h264parser.c
+--- vdr-1.7.0-extensions/h264parser.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-ext-h264-s2ng-speedup/h264parser.c 2009-04-12 23:57:23.000000000 +0200
+@@ -0,0 +1,461 @@
++/*
++ * h264parser.c: a minimalistic H.264 video stream parser
++ *
++ * See the main source file 'vdr.c' for copyright information and
++ * how to reach the author.
++ *
++ * The code was originally written by Reinhard Nissl <rnissl@gmx.de>,
++ * and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de.
++ */
++
++#include "tools.h"
++#include "h264parser.h"
++
++namespace H264
++{
++ // --- cContext ------------------------------------------------------------
++
++ int cContext::GetFramesPerSec(void) const
++ {
++ const cSequenceParameterSet *SPS = ActiveSPS();
++ const cSliceHeader *SH = CurrentSlice();
++ if (!SH || !SPS->timing_info_present_flag || !SPS->time_scale || !SPS->num_units_in_tick)
++ return -1;
++ uint32_t DeltaTfiDivisor;
++ if (SPS->pic_struct_present_flag) {
++ if (!SPS->pic_timing_sei.Defined())
++ return -1;
++ switch (SPS->pic_timing_sei.pic_struct) {
++ case 1:
++ case 2:
++ DeltaTfiDivisor = 1;
++ break;
++ case 0:
++ case 3:
++ case 4:
++ DeltaTfiDivisor = 2;
++ break;
++ case 5:
++ case 6:
++ DeltaTfiDivisor = 3;
++ break;
++ case 7:
++ DeltaTfiDivisor = 4;
++ break;
++ case 8:
++ DeltaTfiDivisor = 6;
++ break;
++ default:
++ return -1;
++ }
++ }
++ else if (!SH->field_pic_flag)
++ DeltaTfiDivisor = 2;
++ else
++ DeltaTfiDivisor = 1;
++
++ double FPS = (double)SPS->time_scale / SPS->num_units_in_tick / DeltaTfiDivisor / (SH->field_pic_flag ? 2 : 1);
++ int FramesPerSec = (int)FPS;
++ if ((FPS - FramesPerSec) >= 0.5)
++ FramesPerSec++;
++ return FramesPerSec;
++ }
++
++ // --- cSimpleBuffer -------------------------------------------------------
++
++ cSimpleBuffer::cSimpleBuffer(int Size)
++ {
++ size = Size;
++ data = new uchar[size];
++ avail = 0;
++ gotten = 0;
++ }
++
++ cSimpleBuffer::~cSimpleBuffer()
++ {
++ delete [] data;
++ }
++
++ int cSimpleBuffer::Put(const uchar *Data, int Count)
++ {
++ if (Count < 0) {
++ if (avail + Count < 0)
++ Count = 0 - avail;
++ if (avail + Count < gotten)
++ Count = gotten - avail;
++ avail += Count;
++ return Count;
++ }
++ if (avail + Count > size)
++ Count = size - avail;
++ memcpy(data + avail, Data, Count);
++ avail += Count;
++ return Count;
++ }
++
++ uchar *cSimpleBuffer::Get(int &Count)
++ {
++ Count = gotten = avail;
++ return data;
++ }
++
++ void cSimpleBuffer::Del(int Count)
++ {
++ if (Count < 0)
++ return;
++ if (Count > gotten) {
++ esyslog("ERROR: invalid Count in H264::cSimpleBuffer::Del: %d (limited to %d)", Count, gotten);
++ Count = gotten;
++ }
++ if (Count < avail)
++ memmove(data, data + Count, avail - Count);
++ avail -= Count;
++ gotten = 0;
++ }
++
++ void cSimpleBuffer::Clear(void)
++ {
++ avail = gotten = 0;
++ }
++
++ // --- cParser -------------------------------------------------------------
++
++ cParser::cParser(bool OmitPicTiming)
++ : nalUnitDataBuffer(1000)
++ {
++ // the above buffer size of 1000 bytes wont hold a complete NAL unit but
++ // should be sufficient for the relevant part used for parsing.
++ omitPicTiming = OmitPicTiming; // only necessary to determine frames per second
++ Reset();
++ }
++
++ void cParser::Reset(void)
++ {
++ context = cContext();
++ nalUnitDataBuffer.Clear();
++ syncing = true;
++ }
++
++ void cParser::ParseSequenceParameterSet(uint8_t *Data, int Count)
++ {
++ cSequenceParameterSet SPS;
++
++ cBitReader br(Data + 1, Count - 1);
++ uint32_t profile_idc = br.u(8);
++ /* uint32_t constraint_set0_flag = */ br.u(1);
++ /* uint32_t constraint_set1_flag = */ br.u(1);
++ /* uint32_t constraint_set2_flag = */ br.u(1);
++ /* uint32_t constraint_set3_flag = */ br.u(1);
++ /* uint32_t reserved_zero_4bits = */ br.u(4);
++ /* uint32_t level_idc = */ br.u(8);
++ SPS.seq_parameter_set_id = br.ue();
++ if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144) {
++ uint32_t chroma_format_idc = br.ue();
++ if (chroma_format_idc == 3) {
++ /* uint32_t residual_colour_transform_flag = */ br.u(1);
++ }
++ /* uint32_t bit_depth_luma_minus8 = */ br.ue();
++ /* uint32_t bit_depth_chroma_minus8 = */ br.ue();
++ /* uint32_t qpprime_y_zero_transform_bypass_flag = */ br.u(1);
++ uint32_t seq_scaling_matrix_present_flag = br.u(1);
++ if (seq_scaling_matrix_present_flag) {
++ for (int i = 0; i < 8; i++) {
++ uint32_t seq_scaling_list_present_flag = br.u(1);
++ if (seq_scaling_list_present_flag) {
++ int sizeOfScalingList = (i < 6) ? 16 : 64;
++ int lastScale = 8;
++ int nextScale = 8;
++ for (int j = 0; j < sizeOfScalingList; j++) {
++ if (nextScale != 0) {
++ int32_t delta_scale = br.se();
++ nextScale = (lastScale + delta_scale + 256) % 256;
++ }
++ lastScale = (nextScale == 0) ? lastScale : nextScale;
++ }
++ }
++ }
++ }
++ }
++ SPS.log2_max_frame_num_minus4(br.ue());
++ SPS.pic_order_cnt_type = br.ue();
++ if (SPS.pic_order_cnt_type == 0)
++ SPS.log2_max_pic_order_cnt_lsb_minus4(br.ue());
++ else if (SPS.pic_order_cnt_type == 1) {
++ SPS.delta_pic_order_always_zero_flag = br.u(1);
++ /* int32_t offset_for_non_ref_pic = */ br.se();
++ /* int32_t offset_for_top_to_bottom_field = */ br.se();
++ uint32_t num_ref_frames_in_pic_order_cnt_cycle = br.ue();
++ for (uint32_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
++ /* int32_t offset_for_ref_frame = */ br.se();
++ }
++ }
++ /* uint32_t num_ref_frames = */ br.ue();
++ /* uint32_t gaps_in_frame_num_value_allowed_flag = */ br.u(1);
++ /* uint32_t pic_width_in_mbs_minus1 = */ br.ue();
++ /* uint32_t pic_height_in_map_units_minus1 = */ br.ue();
++ SPS.frame_mbs_only_flag = br.u(1);
++
++ if (!omitPicTiming) {
++ if (!SPS.frame_mbs_only_flag) {
++ /* uint32_t mb_adaptive_frame_field_flag = */ br.u(1);
++ }
++ /* uint32_t direct_8x8_inference_flag = */ br.u(1);
++ uint32_t frame_cropping_flag = br.u(1);
++ if (frame_cropping_flag) {
++ /* uint32_t frame_crop_left_offset = */ br.ue();
++ /* uint32_t frame_crop_right_offset = */ br.ue();
++ /* uint32_t frame_crop_top_offset = */ br.ue();
++ /* uint32_t frame_crop_bottom_offset = */ br.ue();
++ }
++ uint32_t vui_parameters_present_flag = br.u(1);
++ if (vui_parameters_present_flag) {
++ uint32_t aspect_ratio_info_present_flag = br.u(1);
++ if (aspect_ratio_info_present_flag) {
++ uint32_t aspect_ratio_idc = br.u(8);
++ const uint32_t Extended_SAR = 255;
++ if (aspect_ratio_idc == Extended_SAR) {
++ /* uint32_t sar_width = */ br.u(16);
++ /* uint32_t sar_height = */ br.u(16);
++ }
++ }
++ uint32_t overscan_info_present_flag = br.u(1);
++ if (overscan_info_present_flag) {
++ /* uint32_t overscan_appropriate_flag = */ br.u(1);
++ }
++ uint32_t video_signal_type_present_flag = br.u(1);
++ if (video_signal_type_present_flag) {
++ /* uint32_t video_format = */ br.u(3);
++ /* uint32_t video_full_range_flag = */ br.u(1);
++ uint32_t colour_description_present_flag = br.u(1);
++ if (colour_description_present_flag) {
++ /* uint32_t colour_primaries = */ br.u(8);
++ /* uint32_t transfer_characteristics = */ br.u(8);
++ /* uint32_t matrix_coefficients = */ br.u(8);
++ }
++ }
++ uint32_t chroma_loc_info_present_flag = br.u(1);
++ if (chroma_loc_info_present_flag) {
++ /* uint32_t chroma_sample_loc_type_top_field = */ br.ue();
++ /* uint32_t chroma_sample_loc_type_bottom_field = */ br.ue();
++ }
++ SPS.timing_info_present_flag = br.u(1);
++ if (SPS.timing_info_present_flag) {
++ SPS.num_units_in_tick = br.u(32);
++ SPS.time_scale = br.u(32);
++ SPS.fixed_frame_rate_flag = br.u(1);
++ }
++ SPS.nal_hrd_parameters_present_flag = br.u(1);
++ if (SPS.nal_hrd_parameters_present_flag)
++ hrd_parameters(SPS, br);
++ SPS.vcl_hrd_parameters_present_flag = br.u(1);
++ if (SPS.vcl_hrd_parameters_present_flag)
++ hrd_parameters(SPS, br);
++ if (SPS.nal_hrd_parameters_present_flag || SPS.vcl_hrd_parameters_present_flag) {
++ /* uint32_t low_delay_hrd_flag = */ br.u(1);
++ }
++ SPS.pic_struct_present_flag = br.u(1);
++ }
++ }
++
++ context.Define(SPS);
++ }
++
++ void cParser::hrd_parameters(cSequenceParameterSet &SPS, cBitReader &br)
++ {
++ uint32_t cpb_cnt_minus1 = br.ue();
++ /* uint32_t bit_rate_scale = */ br.u(4);
++ /* uint32_t cpb_size_scale = */ br.u(4);
++ for (uint32_t i = 0; i <= cpb_cnt_minus1; i++) {
++ /* uint32_t bit_rate_value_minus1 = */ br.ue();
++ /* uint32_t cpb_size_value_minus1 = */ br.ue();
++ /* uint32_t cbr_flag = */ br.u(1);
++ }
++ /* uint32_t initial_cpb_removal_delay_length_minus1 = */ br.u(5);
++ SPS.cpb_removal_delay_length_minus1(br.u(5));
++ SPS.dpb_output_delay_length_minus1(br.u(5));
++ /* uint32_t time_offset_length = */ br.u(5);
++ }
++
++ void cParser::ParsePictureParameterSet(uint8_t *Data, int Count)
++ {
++ cPictureParameterSet PPS;
++
++ cBitReader br(Data + 1, Count - 1);
++ PPS.pic_parameter_set_id = br.ue();
++ PPS.seq_parameter_set_id = br.ue();
++ /* uint32_t entropy_coding_mode_flag = */ br.u(1);
++ PPS.pic_order_present_flag = br.u(1);
++
++ context.Define(PPS);
++ }
++
++ void cParser::ParseSlice(uint8_t *Data, int Count)
++ {
++ cSliceHeader SH;
++
++ cBitReader br(Data + 1, Count - 1);
++ SH.nal_ref_idc(Data[0] >> 5);
++ SH.nal_unit_type(Data[0] & 0x1F);
++ /* uint32_t first_mb_in_slice = */ br.ue();
++ SH.slice_type = br.ue();
++ SH.pic_parameter_set_id = br.ue();
++
++ context.ActivatePPS(SH.pic_parameter_set_id);
++ const cSequenceParameterSet *SPS = context.ActiveSPS();
++
++ SH.frame_num = br.u(SPS->log2_max_frame_num());
++ if (!SPS->frame_mbs_only_flag) {
++ SH.field_pic_flag = br.u(1);
++ if (SH.field_pic_flag)
++ SH.bottom_field_flag = br.u(1);
++ }
++ if (SH.nal_unit_type() == 5)
++ SH.idr_pic_id = br.ue();
++ if (SPS->pic_order_cnt_type == 0) {
++ SH.pic_order_cnt_lsb = br.u(SPS->log2_max_pic_order_cnt_lsb());
++ const cPictureParameterSet *PPS = context.ActivePPS();
++ if (PPS->pic_order_present_flag && !SH.field_pic_flag)
++ SH.delta_pic_order_cnt_bottom = br.se();
++ }
++ if (SPS->pic_order_cnt_type == 1 && !SPS->delta_pic_order_always_zero_flag) {
++ SH.delta_pic_order_cnt[0] = br.se();
++ const cPictureParameterSet *PPS = context.ActivePPS();
++ if (PPS->pic_order_present_flag && !SH.field_pic_flag)
++ SH.delta_pic_order_cnt[1] = br.se();
++ }
++
++ context.Define(SH);
++ }
++
++ void cParser::ParseSEI(uint8_t *Data, int Count)
++ {
++ // currently only used to determine frames per second
++ if (omitPicTiming)
++ return;
++ cBitReader br(Data + 1, Count - 1);
++ do
++ sei_message(br);
++ while (br.GetBytesAvail());
++ }
++
++ void cParser::sei_message(cBitReader &br)
++ {
++ uint32_t payloadType = 0;
++ while (1) {
++ uint32_t last_payload_type_byte = br.u(8);
++ payloadType += last_payload_type_byte;
++ if (last_payload_type_byte != 0xFF)
++ break;
++ }
++ uint32_t payloadSize = 0;
++ while (1) {
++ uint32_t last_payload_size_byte = br.u(8);
++ payloadSize += last_payload_size_byte;
++ if (last_payload_size_byte != 0xFF)
++ break;
++ }
++ sei_payload(payloadType, payloadSize, br);
++ }
++
++ void cParser::sei_payload(uint32_t payloadType, uint32_t payloadSize, cBitReader &br)
++ {
++ const cBitReader::cBookMark BookMark = br.BookMark();
++ switch (payloadType) {
++ case 0:
++ buffering_period(payloadSize, br);
++ break;
++ case 1:
++ pic_timing(payloadSize, br);
++ break;
++ }
++ // instead of dealing with trailing bits in each message
++ // go back to start of message and skip it completely
++ br.BookMark(BookMark);
++ reserved_sei_message(payloadSize, br);
++ }
++
++ void cParser::buffering_period(uint32_t payloadSize, cBitReader &br)
++ {
++ uint32_t seq_parameter_set_id = br.ue();
++
++ context.ActivateSPS(seq_parameter_set_id);
++ }
++
++ void cParser::pic_timing(uint32_t payloadSize, cBitReader &br)
++ {
++ cPictureTiming PT;
++
++ const cSequenceParameterSet *SPS = context.ActiveSPS();
++ if (!SPS)
++ return;
++ uint32_t CpbDpbDelaysPresentFlag = SPS->nal_hrd_parameters_present_flag || SPS->vcl_hrd_parameters_present_flag;
++ if (CpbDpbDelaysPresentFlag) {
++ /* uint32_t cpb_removal_delay = */ br.u(SPS->cpb_removal_delay_length());
++ /* uint32_t dpb_output_delay = */ br.u(SPS->dpb_output_delay_length());
++ }
++ if (SPS->pic_struct_present_flag) {
++ PT.pic_struct = br.u(4);
++ }
++
++ context.Define(PT);
++ }
++
++ void cParser::reserved_sei_message(uint32_t payloadSize, cBitReader &br)
++ {
++ for (uint32_t i = 0; i < payloadSize; i++) {
++ /* uint32_t reserved_sei_message_payload_byte = */ br.u(8);
++ }
++ }
++
++ void cParser::PutNalUnitData(const uchar *Data, int Count)
++ {
++ int n = nalUnitDataBuffer.Put(Data, Count);
++ // typically less than a complete NAL unit are needed for parsing the
++ // relevant data, so simply ignore the overflow condition.
++ if (false && n != Count)
++ esyslog("ERROR: H264::cParser::PutNalUnitData(): NAL unit data buffer overflow");
++ }
++
++ void cParser::Process()
++ {
++ // nalUnitDataBuffer contains the head of the current NAL unit -- let's parse it
++ int Count = 0;
++ uchar *Data = nalUnitDataBuffer.Get(Count);
++ if (Data && Count >= 4) {
++ if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01) {
++ int nal_unit_type = Data[3] & 0x1F;
++ try {
++ switch (nal_unit_type) {
++ case 1: // coded slice of a non-IDR picture
++ case 2: // coded slice data partition A
++ case 5: // coded slice of an IDR picture
++ ParseSlice(Data + 3, Count - 3);
++ break;
++ case 6: // supplemental enhancement information (SEI)
++ ParseSEI(Data + 3, Count - 3);
++ break;
++ case 7: // sequence parameter set
++ syncing = false; // from now on, we should get reliable results
++ ParseSequenceParameterSet(Data + 3, Count - 3);
++ break;
++ case 8: // picture parameter set
++ ParsePictureParameterSet(Data + 3, Count - 3);
++ break;
++ }
++ }
++ catch (cException *e) {
++ if (!syncing) // suppress typical error messages while syncing
++ esyslog(e->Message());
++ delete e;
++ }
++ }
++ else if (!syncing)
++ esyslog("ERROR: H264::cParser::Process(): NAL unit data buffer content is invalid");
++ }
++ else if (!syncing)
++ esyslog("ERROR: H264::cParser::Process(): NAL unit data buffer content is too short");
++ // reset the buffer for the next NAL unit
++ nalUnitDataBuffer.Clear();
++ }
++}
++
+diff -ruNp vdr-1.7.0-extensions/h264parser.h vdr-1.7.0-ext-h264-s2ng-speedup/h264parser.h
+--- vdr-1.7.0-extensions/h264parser.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-ext-h264-s2ng-speedup/h264parser.h 2009-04-12 23:57:23.000000000 +0200
+@@ -0,0 +1,397 @@
++/*
++ * h264parser.h: a minimalistic H.264 video stream parser
++ *
++ * See the main source file 'vdr.c' for copyright information and
++ * how to reach the author.
++ */
++
++#ifndef __H264PARSER_H
++#define __H264PARSER_H
++
++namespace H264
++{
++ // --- cException ----------------------------------------------------------
++
++ class cException {
++ private:
++ cString message;
++ public:
++ cException(const cString &Message) { message = Message; }
++ const cString &Message(void) const { return message; }
++ };
++
++ // --- cBitReader ----------------------------------------------------------
++
++ class cBitReader {
++ public:
++ class cBookMark {
++ private:
++ uint8_t *data;
++ int count;
++ uint32_t bits;
++ uint32_t bitsAvail;
++ int countZeros;
++ cBookMark(void) {}
++ friend class cBitReader;
++ };
++ private:
++ cBookMark bm;
++ uint8_t NextByte(void);
++ uint32_t ReadBits(uint32_t n);
++ public:
++ cBitReader(uint8_t *Data, int Count);
++ uint32_t u(uint32_t n) { return ReadBits(n); } // read n bits as unsigned number
++ uint32_t ue(void); // read Exp-Golomb coded unsigned number
++ int32_t se(void); // read Exp-Golomb coded signed number
++ uint32_t GetBitsAvail(void) { return (bm.bitsAvail & 0x07); }
++ bool GetBytesAvail(void) { return (bm.count > 0); }
++ const cBookMark BookMark(void) const { return bm; }
++ void BookMark(const cBookMark &b) { bm = b; }
++ };
++
++ inline cBitReader::cBitReader(unsigned char *Data, int Count)
++ {
++ bm.data = Data;
++ bm.count = Count;
++ bm.bitsAvail = 0;
++ bm.countZeros = 0;
++ }
++
++ inline uint8_t cBitReader::NextByte(void)
++ {
++ if (bm.count < 1) // there is no more data left in this NAL unit
++ throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data");
++ // detect 00 00 00, 00 00 01 and 00 00 03 and handle them
++ if (*bm.data == 0x00) {
++ if (bm.countZeros >= 3) // 00 00 00: the current NAL unit should have been terminated already before this sequence
++ throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data");
++ // increase the zero counter as we have a zero byte
++ bm.countZeros++;
++ }
++ else {
++ if (bm.countZeros >= 2) {
++ if (*bm.data == 0x01) // 00 00 01: the current NAL unit should have been terminated already before this sequence
++ throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data");
++ if (*bm.data == 0x03) {
++ // 00 00 03 xx: the emulation prevention byte 03 needs to be removed and xx must be returned
++ if (bm.count < 2)
++ throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data");
++ // drop 03 and xx will be returned below
++ bm.count--;
++ bm.data++;
++ }
++ }
++ // reset the zero counter as we had a non zero byte
++ bm.countZeros = 0;
++ }
++ bm.count--;
++ return *bm.data++;
++ }
++
++ inline uint32_t cBitReader::ReadBits(uint32_t n)
++ {
++ // fill the "shift register" bits with sufficient data
++ while (n > bm.bitsAvail) {
++ bm.bits <<= 8;
++ bm.bits |= NextByte();
++ bm.bitsAvail += 8;
++ if (bm.bitsAvail > 24) { // a further turn will overflow bitbuffer
++ if (n <= bm.bitsAvail)
++ break; // service non overflowing request
++ if (n <= 32) // split overflowing reads into concatenated reads
++ return (ReadBits(16) << 16) | ReadBits(n - 16);
++ // cannot read more than 32 bits at once
++ throw new cException("ERROR: H264::cBitReader::ReadBits(): bitbuffer overflow");
++ }
++ }
++ // return n most significant bits
++ bm.bitsAvail -= n;
++ return (bm.bits >> bm.bitsAvail) & (((uint32_t)1 << n) - 1);
++ }
++
++ inline uint32_t cBitReader::ue(void)
++ {
++ // read and decode an Exp-Golomb coded unsigned number
++ //
++ // bitstring resulting number
++ // 1 0
++ // 0 1 x 1 ... 2
++ // 0 0 1 x y 3 ... 6
++ // 0 0 0 1 x y z 7 ... 14
++ // ...
++ int LeadingZeroBits = 0;
++ while (ReadBits(1) == 0)
++ LeadingZeroBits++;
++ if (LeadingZeroBits == 0)
++ return 0;
++ if (LeadingZeroBits >= 32)
++ throw new cException("ERROR: H264::cBitReader::ue(): overflow");
++ return ((uint32_t)1 << LeadingZeroBits) - 1 + ReadBits(LeadingZeroBits);
++ }
++
++ inline int32_t cBitReader::se(void)
++ {
++ // read and decode an Exp-Golomb coded signed number
++ //
++ // unsigned value resulting signed value
++ // 0 0
++ // 1 +1
++ // 2 -1
++ // 3 +2
++ // 4 -2
++ // ...
++ uint32_t r = ue();
++ if (r > 0xFFFFFFFE)
++ throw new cException("ERROR: H264::cBitReader::se(): overflow");
++ return (1 - 2 * (r & 1)) * ((r + 1) / 2);
++ }
++
++ // --- cPictureTiming ------------------------------------------------------
++
++ class cPictureTiming {
++ private:
++ friend class cContext;
++ bool defined;
++ public:
++ cPictureTiming(void) { memset(this, 0, sizeof (*this)); }
++ bool Defined(void) const { return defined; }
++ uint32_t pic_struct;
++ };
++
++ // --- cSequenceParameterSet -----------------------------------------------
++
++ class cSequenceParameterSet {
++ private:
++ friend class cContext;
++ bool defined;
++ uint32_t log2MaxFrameNum;
++ uint32_t log2MaxPicOrderCntLsb;
++ uint32_t cpbRemovalDelayLength;
++ uint32_t dpbOutputDelayLength;
++ public:
++ cSequenceParameterSet(void);
++ bool Defined(void) { return defined; }
++ void log2_max_frame_num_minus4(uint32_t Value) { log2MaxFrameNum = Value + 4; }
++ uint32_t log2_max_frame_num_minus4(void) const { return log2MaxFrameNum - 4; }
++ uint32_t log2_max_frame_num(void) const { return log2MaxFrameNum; }
++ void log2_max_pic_order_cnt_lsb_minus4(uint32_t Value) { log2MaxPicOrderCntLsb = Value + 4; }
++ uint32_t log2_max_pic_order_cnt_lsb_minus4(void) const { return log2MaxPicOrderCntLsb - 4; }
++ uint32_t log2_max_pic_order_cnt_lsb(void) const { return log2MaxPicOrderCntLsb; }
++ void cpb_removal_delay_length_minus1(uint32_t Value) { cpbRemovalDelayLength = Value + 1; }
++ uint32_t cpb_removal_delay_length_minus1(void) const { return cpbRemovalDelayLength - 1; }
++ uint32_t cpb_removal_delay_length(void) const { return cpbRemovalDelayLength; }
++ void dpb_output_delay_length_minus1(uint32_t Value) { dpbOutputDelayLength = Value + 1; }
++ uint32_t dpb_output_delay_length_minus1(void) const { return dpbOutputDelayLength - 1; }
++ uint32_t dpb_output_delay_length(void) const { return dpbOutputDelayLength; }
++ uint32_t seq_parameter_set_id;
++ uint32_t pic_order_cnt_type;
++ uint32_t delta_pic_order_always_zero_flag;
++ uint32_t frame_mbs_only_flag;
++ uint32_t timing_info_present_flag;
++ uint32_t num_units_in_tick;
++ uint32_t time_scale;
++ uint32_t fixed_frame_rate_flag;
++ uint32_t nal_hrd_parameters_present_flag;
++ uint32_t vcl_hrd_parameters_present_flag;
++ uint32_t pic_struct_present_flag;
++ cPictureTiming pic_timing_sei;
++ };
++
++ inline cSequenceParameterSet::cSequenceParameterSet(void)
++ {
++ memset(this, 0, sizeof (*this));
++ log2_max_frame_num_minus4(0);
++ log2_max_pic_order_cnt_lsb_minus4(0);
++ cpb_removal_delay_length_minus1(23);
++ dpb_output_delay_length_minus1(23);
++ }
++
++ // --- cPictureParameterSet ------------------------------------------------
++
++ class cPictureParameterSet {
++ private:
++ friend class cContext;
++ bool defined;
++ public:
++ cPictureParameterSet(void) { memset(this, 0, sizeof (*this)); }
++ bool Defined(void) { return defined; }
++ uint32_t pic_parameter_set_id;
++ uint32_t seq_parameter_set_id;
++ uint32_t pic_order_present_flag;
++ };
++
++ // --- cSliceHeader --------------------------------------------------------
++
++ class cSliceHeader {
++ private:
++ friend class cContext;
++ bool defined;
++ bool isFirstSliceOfCurrentAccessUnit;
++ uint32_t picOrderCntType;
++ uint32_t nalRefIdc;
++ uint32_t nalUnitType;
++ public:
++ cSliceHeader(void) { memset(this, 0, sizeof (*this)); }
++ bool Defined(void) const { return defined; }
++ bool IsFirstSliceOfCurrentAccessUnit(void) const { return isFirstSliceOfCurrentAccessUnit; }
++ void nal_ref_idc(uint32_t Value) { nalRefIdc = Value; }
++ uint32_t nal_ref_idc(void) const { return nalRefIdc; }
++ void nal_unit_type(uint32_t Value) { nalUnitType = Value; }
++ uint32_t nal_unit_type(void) const { return nalUnitType; }
++ uint32_t slice_type;
++ uint32_t pic_parameter_set_id;
++ uint32_t frame_num;
++ uint32_t field_pic_flag;
++ uint32_t bottom_field_flag;
++ uint32_t idr_pic_id;
++ uint32_t pic_order_cnt_lsb;
++ int32_t delta_pic_order_cnt_bottom;
++ int32_t delta_pic_order_cnt[2];
++ enum eAccessUnitType {
++ Frame = 0,
++ TopField,
++ BottomField
++ };
++ eAccessUnitType GetAccessUnitType() const { return (eAccessUnitType)(field_pic_flag + bottom_field_flag); }
++ };
++
++ // --- cContext ------------------------------------------------------------
++
++ class cContext {
++ private:
++ cSequenceParameterSet spsStore[32];
++ cPictureParameterSet ppsStore[256];
++ cSequenceParameterSet *sps; // active Sequence Parameter Set
++ cPictureParameterSet *pps; // active Picture Parameter Set
++ cSliceHeader sh;
++ public:
++ cContext(void) { sps = 0; pps = 0; }
++ void Define(cSequenceParameterSet &SPS);
++ void Define(cPictureParameterSet &PPS);
++ void Define(cSliceHeader &SH);
++ void Define(cPictureTiming &PT);
++ void ActivateSPS(uint32_t ID);
++ void ActivatePPS(uint32_t ID);
++ const cSequenceParameterSet *ActiveSPS(void) const { return sps; }
++ const cPictureParameterSet *ActivePPS(void) const { return pps; }
++ const cSliceHeader *CurrentSlice(void) const { return sh.Defined() ? &sh : 0; }
++ int GetFramesPerSec(void) const;
++ };
++
++ inline void cContext::ActivateSPS(uint32_t ID)
++ {
++ if (ID >= (sizeof (spsStore) / sizeof (*spsStore)))
++ throw new cException("ERROR: H264::cContext::ActivateSPS(): id out of range");
++ if (!spsStore[ID].Defined())
++ throw new cException("ERROR: H264::cContext::ActivateSPS(): requested SPS is undefined");
++ sps = &spsStore[ID];
++ }
++
++ inline void cContext::ActivatePPS(uint32_t ID)
++ {
++ if (ID >= (sizeof (ppsStore) / sizeof (*ppsStore)))
++ throw new cException("ERROR: H264::cContext::ActivatePPS(): id out of range");
++ if (!ppsStore[ID].Defined())
++ throw new cException("ERROR: H264::cContext::ActivatePPS(): requested PPS is undefined");
++ pps = &ppsStore[ID];
++ ActivateSPS(pps->seq_parameter_set_id);
++ }
++
++ inline void cContext::Define(cSequenceParameterSet &SPS)
++ {
++ if (SPS.seq_parameter_set_id >= (sizeof (spsStore) / sizeof (*spsStore)))
++ throw new cException("ERROR: H264::cContext::DefineSPS(): id out of range");
++ SPS.defined = true;
++ spsStore[SPS.seq_parameter_set_id] = SPS;
++ }
++
++ inline void cContext::Define(cPictureParameterSet &PPS)
++ {
++ if (PPS.pic_parameter_set_id >= (sizeof (ppsStore) / sizeof (*ppsStore)))
++ throw new cException("ERROR: H264::cContext::DefinePPS(): id out of range");
++ PPS.defined = true;
++ ppsStore[PPS.pic_parameter_set_id] = PPS;
++ }
++
++ inline void cContext::Define(cSliceHeader &SH)
++ {
++ SH.defined = true;
++ SH.picOrderCntType = ActiveSPS()->pic_order_cnt_type;
++
++ // ITU-T Rec. H.264 (03/2005): 7.4.1.2.4
++ SH.isFirstSliceOfCurrentAccessUnit = !sh.Defined()
++ || (sh.frame_num != SH.frame_num)
++ || (sh.pic_parameter_set_id != SH.pic_parameter_set_id)
++ || (sh.field_pic_flag != SH.field_pic_flag)
++ || (sh.bottom_field_flag != SH.bottom_field_flag)
++ || (sh.nalRefIdc != SH.nalRefIdc
++ && (sh.nalRefIdc == 0 || SH.nalRefIdc == 0))
++ || (sh.picOrderCntType == 0 && SH.picOrderCntType == 0
++ && (sh.pic_order_cnt_lsb != SH.pic_order_cnt_lsb
++ || sh.delta_pic_order_cnt_bottom != SH.delta_pic_order_cnt_bottom))
++ || (sh.picOrderCntType == 1 && SH.picOrderCntType == 1
++ && (sh.delta_pic_order_cnt[0] != SH.delta_pic_order_cnt[0]
++ || sh.delta_pic_order_cnt[1] != SH.delta_pic_order_cnt[1]))
++ || (sh.nalUnitType != SH.nalUnitType
++ && (sh.nalUnitType == 5 || SH.nalUnitType == 5))
++ || (sh.nalUnitType == 5 && SH.nalUnitType == 5
++ && sh.idr_pic_id != SH.idr_pic_id);
++
++ sh = SH;
++ }
++
++ inline void cContext::Define(cPictureTiming &PT)
++ {
++ PT.defined = true;
++ ((cSequenceParameterSet *)ActiveSPS())->pic_timing_sei = PT;
++ }
++
++ // --- cSimpleBuffer -------------------------------------------------------
++
++ class cSimpleBuffer {
++ private:
++ uchar *data;
++ int size;
++ int avail;
++ int gotten;
++ public:
++ cSimpleBuffer(int Size);
++ ~cSimpleBuffer();
++ int Size(void) { return size; }
++ int Available(void) { return avail; }
++ int Free(void) { return size - avail; }
++ int Put(const uchar *Data, int Count);
++ uchar *Get(int &Count);
++ void Del(int Count);
++ void Clear(void);
++ };
++
++ // --- cParser -------------------------------------------------------------
++
++ class cParser {
++ private:
++ bool syncing;
++ bool omitPicTiming;
++ cContext context;
++ cSimpleBuffer nalUnitDataBuffer;
++ void hrd_parameters(cSequenceParameterSet &SPS, cBitReader &br);
++ void ParseSequenceParameterSet(uint8_t *Data, int Count);
++ void ParsePictureParameterSet(uint8_t *Data, int Count);
++ void ParseSlice(uint8_t *Data, int Count);
++ void reserved_sei_message(uint32_t payloadSize, cBitReader &br);
++ void pic_timing(uint32_t payloadSize, cBitReader &br);
++ void buffering_period(uint32_t payloadSize, cBitReader &br);
++ void sei_payload(uint32_t payloadType, uint32_t payloadSize, cBitReader &br);
++ void sei_message(cBitReader &br);
++ void ParseSEI(uint8_t *Data, int Count);
++ public:
++ cParser(bool OmitPicTiming = true);
++ const cContext &Context(void) const { return context; }
++ void PutNalUnitData(const uchar *Data, int Count);
++ void Reset(void);
++ void Process(void);
++ };
++}
++
++#endif // __H264PARSER_H
++
+diff -ruNp vdr-1.7.0-extensions/libsi/util.c vdr-1.7.0-ext-h264-s2ng-speedup/libsi/util.c
+--- vdr-1.7.0-extensions/libsi/util.c 2006-02-18 12:17:50.000000000 +0100
++++ vdr-1.7.0-ext-h264-s2ng-speedup/libsi/util.c 2009-04-12 23:57:23.000000000 +0200
+@@ -219,58 +219,73 @@ time_t DVBTime::getDuration(unsigned cha
+
+ //taken and adapted from libdtv, (c) Rolf Hakenes
+ // CRC32 lookup table for polynomial 0x04c11db7
++// swapped bytes to avoid one shift operation in CRC loop (c) Reinhard Nissl
+ u_int32_t CRC32::crc_table[256] = {
+- 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+- 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+- 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
+- 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+- 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
+- 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+- 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
+- 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+- 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
+- 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+- 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+- 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+- 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
+- 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+- 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
+- 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+- 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
+- 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+- 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
+- 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+- 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+- 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+- 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
+- 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+- 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
+- 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+- 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
+- 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+- 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
+- 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+- 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+- 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+- 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
+- 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+- 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
+- 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+- 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
+- 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+- 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
+- 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+- 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+- 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+- 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4};
++ 0x00000000, 0xb71dc104, 0x6e3b8209, 0xd926430d, 0xdc760413, 0x6b6bc517,
++ 0xb24d861a, 0x0550471e, 0xb8ed0826, 0x0ff0c922, 0xd6d68a2f, 0x61cb4b2b,
++ 0x649b0c35, 0xd386cd31, 0x0aa08e3c, 0xbdbd4f38, 0x70db114c, 0xc7c6d048,
++ 0x1ee09345, 0xa9fd5241, 0xacad155f, 0x1bb0d45b, 0xc2969756, 0x758b5652,
++ 0xc836196a, 0x7f2bd86e, 0xa60d9b63, 0x11105a67, 0x14401d79, 0xa35ddc7d,
++ 0x7a7b9f70, 0xcd665e74, 0xe0b62398, 0x57abe29c, 0x8e8da191, 0x39906095,
++ 0x3cc0278b, 0x8bdde68f, 0x52fba582, 0xe5e66486, 0x585b2bbe, 0xef46eaba,
++ 0x3660a9b7, 0x817d68b3, 0x842d2fad, 0x3330eea9, 0xea16ada4, 0x5d0b6ca0,
++ 0x906d32d4, 0x2770f3d0, 0xfe56b0dd, 0x494b71d9, 0x4c1b36c7, 0xfb06f7c3,
++ 0x2220b4ce, 0x953d75ca, 0x28803af2, 0x9f9dfbf6, 0x46bbb8fb, 0xf1a679ff,
++ 0xf4f63ee1, 0x43ebffe5, 0x9acdbce8, 0x2dd07dec, 0x77708634, 0xc06d4730,
++ 0x194b043d, 0xae56c539, 0xab068227, 0x1c1b4323, 0xc53d002e, 0x7220c12a,
++ 0xcf9d8e12, 0x78804f16, 0xa1a60c1b, 0x16bbcd1f, 0x13eb8a01, 0xa4f64b05,
++ 0x7dd00808, 0xcacdc90c, 0x07ab9778, 0xb0b6567c, 0x69901571, 0xde8dd475,
++ 0xdbdd936b, 0x6cc0526f, 0xb5e61162, 0x02fbd066, 0xbf469f5e, 0x085b5e5a,
++ 0xd17d1d57, 0x6660dc53, 0x63309b4d, 0xd42d5a49, 0x0d0b1944, 0xba16d840,
++ 0x97c6a5ac, 0x20db64a8, 0xf9fd27a5, 0x4ee0e6a1, 0x4bb0a1bf, 0xfcad60bb,
++ 0x258b23b6, 0x9296e2b2, 0x2f2bad8a, 0x98366c8e, 0x41102f83, 0xf60dee87,
++ 0xf35da999, 0x4440689d, 0x9d662b90, 0x2a7bea94, 0xe71db4e0, 0x500075e4,
++ 0x892636e9, 0x3e3bf7ed, 0x3b6bb0f3, 0x8c7671f7, 0x555032fa, 0xe24df3fe,
++ 0x5ff0bcc6, 0xe8ed7dc2, 0x31cb3ecf, 0x86d6ffcb, 0x8386b8d5, 0x349b79d1,
++ 0xedbd3adc, 0x5aa0fbd8, 0xeee00c69, 0x59fdcd6d, 0x80db8e60, 0x37c64f64,
++ 0x3296087a, 0x858bc97e, 0x5cad8a73, 0xebb04b77, 0x560d044f, 0xe110c54b,
++ 0x38368646, 0x8f2b4742, 0x8a7b005c, 0x3d66c158, 0xe4408255, 0x535d4351,
++ 0x9e3b1d25, 0x2926dc21, 0xf0009f2c, 0x471d5e28, 0x424d1936, 0xf550d832,
++ 0x2c769b3f, 0x9b6b5a3b, 0x26d61503, 0x91cbd407, 0x48ed970a, 0xfff0560e,
++ 0xfaa01110, 0x4dbdd014, 0x949b9319, 0x2386521d, 0x0e562ff1, 0xb94beef5,
++ 0x606dadf8, 0xd7706cfc, 0xd2202be2, 0x653deae6, 0xbc1ba9eb, 0x0b0668ef,
++ 0xb6bb27d7, 0x01a6e6d3, 0xd880a5de, 0x6f9d64da, 0x6acd23c4, 0xddd0e2c0,
++ 0x04f6a1cd, 0xb3eb60c9, 0x7e8d3ebd, 0xc990ffb9, 0x10b6bcb4, 0xa7ab7db0,
++ 0xa2fb3aae, 0x15e6fbaa, 0xccc0b8a7, 0x7bdd79a3, 0xc660369b, 0x717df79f,
++ 0xa85bb492, 0x1f467596, 0x1a163288, 0xad0bf38c, 0x742db081, 0xc3307185,
++ 0x99908a5d, 0x2e8d4b59, 0xf7ab0854, 0x40b6c950, 0x45e68e4e, 0xf2fb4f4a,
++ 0x2bdd0c47, 0x9cc0cd43, 0x217d827b, 0x9660437f, 0x4f460072, 0xf85bc176,
++ 0xfd0b8668, 0x4a16476c, 0x93300461, 0x242dc565, 0xe94b9b11, 0x5e565a15,
++ 0x87701918, 0x306dd81c, 0x353d9f02, 0x82205e06, 0x5b061d0b, 0xec1bdc0f,
++ 0x51a69337, 0xe6bb5233, 0x3f9d113e, 0x8880d03a, 0x8dd09724, 0x3acd5620,
++ 0xe3eb152d, 0x54f6d429, 0x7926a9c5, 0xce3b68c1, 0x171d2bcc, 0xa000eac8,
++ 0xa550add6, 0x124d6cd2, 0xcb6b2fdf, 0x7c76eedb, 0xc1cba1e3, 0x76d660e7,
++ 0xaff023ea, 0x18ede2ee, 0x1dbda5f0, 0xaaa064f4, 0x738627f9, 0xc49be6fd,
++ 0x09fdb889, 0xbee0798d, 0x67c63a80, 0xd0dbfb84, 0xd58bbc9a, 0x62967d9e,
++ 0xbbb03e93, 0x0cadff97, 0xb110b0af, 0x060d71ab, 0xdf2b32a6, 0x6836f3a2,
++ 0x6d66b4bc, 0xda7b75b8, 0x035d36b5, 0xb440f7b1};
++
++inline void swap_bytes(u_int32_t &crc)
++{
++ unsigned char a = crc >> 24;
++ unsigned char b = crc >> 16;
++ unsigned char c = crc >> 8;
++ unsigned char d = crc;
++
++ crc = ((d << 8 | c) << 8 | b) << 8 | a;
++}
+
+ u_int32_t CRC32::crc32 (const char *d, int len, u_int32_t crc)
+ {
+ register int i;
+ const unsigned char *u=(unsigned char*)d; // Saves '& 0xff'
+
++ swap_bytes(crc);
++
+ for (i=0; i<len; i++)
+- crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *u++)];
++ crc = (crc >> 8) ^ crc_table[(unsigned char)crc ^ *u++];
++
++ swap_bytes(crc);
+
+ return crc;
+ }
+diff -ruNp vdr-1.7.0-extensions/lirc.c vdr-1.7.0-ext-h264-s2ng-speedup/lirc.c
+--- vdr-1.7.0-extensions/lirc.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/lirc.c 2009-04-12 23:57:23.000000000 +0200
+@@ -71,17 +71,48 @@ void cLircRemote::Action(void)
+ bool repeat = false;
+ int timeout = -1;
+
++#ifdef USE_LIRCSETTINGS
++ if (Setup.LircPriorityBoost)
++ SetPriority(GetPriority() - Setup.LircPriorityBoost);
++#endif /* LIRCSETTINGS */
+ while (Running() && f >= 0) {
+
+ bool ready = cFile::FileReady(f, timeout);
++#ifdef USE_LIRCSETTINGS
++ int ret = -1;
++ if (ready) {
++ // read one line of the line oriented lirc protocol
++ for (ret = 0; ret < (int)sizeof(buf); ret++) {
++ int ch = readchar(f);
++ if (ch < 0) {
++ ret = -1;
++ break;
++ }
++ if (ch == '\n') {
++ buf[ret++] = '\0';
++ break;
++ }
++ buf[ret] = ch;
++ }
++ }
++#else
+ int ret = ready ? safe_read(f, buf, sizeof(buf)) : -1;
++#endif /* LIRCSETTINGS */
+
+ if (ready && ret <= 0 ) {
++#ifdef USE_LIRCSETTINGS
++ esyslog("ERROR: lircd connection broken, trying to reconnect every %.1f seconds", float(Setup.LircReconnectDelay) / 1000);
++#else
+ esyslog("ERROR: lircd connection broken, trying to reconnect every %.1f seconds", float(RECONNECTDELAY) / 1000);
++#endif /* LIRCSETTINGS */
+ close(f);
+ f = -1;
+ while (Running() && f < 0) {
++#ifdef USE_LIRCSETTINGS
++ cCondWait::SleepMs(Setup.LircReconnectDelay);
++#else
+ cCondWait::SleepMs(RECONNECTDELAY);
++#endif /* LIRCSETTINGS */
+ if (Connect()) {
+ isyslog("reconnected to lircd");
+ break;
+@@ -98,7 +129,7 @@ void cLircRemote::Action(void)
+ }
+ if (count == 0) {
+ #ifdef USE_LIRCSETTINGS
+- if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < (unsigned int)Setup.LircRepeatDelay)
++ if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < (1000 / (unsigned int)Setup.LircPushFreq))
+ #else
+ if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < REPEATDELAY)
+ #endif /* LIRCSETTINGS */
+@@ -112,7 +143,7 @@ void cLircRemote::Action(void)
+ }
+ else {
+ #ifdef USE_LIRCSETTINGS
+- if (LastTime.Elapsed() < (unsigned int)Setup.LircRepeatFreq)
++ if (LastTime.Elapsed() < (1000 / (unsigned int)Setup.LircRepeatFreq))
+ #else
+ if (LastTime.Elapsed() < REPEATFREQ)
+ #endif /* LIRCSETTINGS */
+diff -ruNp vdr-1.7.0-extensions/Make.config.template vdr-1.7.0-ext-h264-s2ng-speedup/Make.config.template
+--- vdr-1.7.0-extensions/Make.config.template 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/Make.config.template 2009-04-12 23:57:23.000000000 +0200
+@@ -59,7 +59,6 @@ CUTTIME = 1
+ DDEPGENTRY = 1
+ #DELTIMESHIFTREC = 1
+ DOLBYINREC = 1
+-#DVBPLAYER = 1
+ #DVBSETUP = 1
+ #DVDARCHIVE = 1
+ #DVDCHAPJUMP = 1
+@@ -90,7 +89,6 @@ SETTIME = 1
+ #SOURCECAPS = 1
+ #SORTRECORDS = 1
+ #STREAMDEVEXT = 1
+-#SYNCEARLY = 1
+ #TIMERCMD = 1
+ #TIMERINFO = 1
+ #TTXTSUBS = 1
+@@ -149,10 +147,6 @@ ifdef DOLBYINREC
+ DEFINES += -DUSE_DOLBYINREC
+ endif
+
+-ifdef DVBPLAYER
+-DEFINES += -DUSE_DVBPLAYER
+-endif
+-
+ ifdef DVBSETUP
+ DEFINES += -DUSE_DVBSETUP
+ endif
+@@ -269,10 +263,6 @@ ifdef STREAMDEVEXT
+ DEFINES += -DUSE_STREAMDEVEXT
+ endif
+
+-ifdef SYNCEARLY
+-DEFINES += -DUSE_SYNCEARLY
+-endif
+-
+ ifdef TIMERCMD
+ DEFINES += -DUSE_TIMERCMD
+ endif
+diff -ruNp vdr-1.7.0-extensions/Makefile vdr-1.7.0-ext-h264-s2ng-speedup/Makefile
+--- vdr-1.7.0-extensions/Makefile 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/Makefile 2009-04-12 23:57:23.000000000 +0200
+@@ -41,7 +41,7 @@ OBJS = audio.o channels.o ci.o config.o
+ lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\
+ receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o shutdown.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
++ timers.o tools.o transfer.o vdr.o videodir.o h264parser.o
+
+ ifdef WAREAGLEICON
+ OBJS += iconpatch.o
+diff -ruNp vdr-1.7.0-extensions/menu.c vdr-1.7.0-ext-h264-s2ng-speedup/menu.c
+--- vdr-1.7.0-extensions/menu.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/menu.c 2009-04-12 23:57:23.000000000 +0200
+@@ -249,17 +249,17 @@ void cMenuEditChannel::Setup(void)
+ Add(new cMenuEditStrItem( tr("Name"), name, sizeof(name)));
+ Add(new cMenuEditSrcItem( tr("Source"), &data.source));
+ Add(new cMenuEditIntItem( tr("Frequency"), &data.frequency));
+- Add(new cMenuEditIntItem( tr("Vpid"), &data.vpid, 0, 0x1FFF));
+- Add(new cMenuEditIntItem( tr("Ppid"), &data.ppid, 0, 0x1FFF));
++ Add(new cMenuEditIntItem( tr("Vpid"), &data.vpid, 0, 0x1FFF));
++ Add(new cMenuEditIntItem( tr("Ppid"), &data.ppid, 0, 0x1FFF));
+ Add(new cMenuEditIntItem( tr("Apid1"), &data.apids[0], 0, 0x1FFF));
+ Add(new cMenuEditIntItem( tr("Apid2"), &data.apids[1], 0, 0x1FFF));
+ Add(new cMenuEditIntItem( tr("Dpid1"), &data.dpids[0], 0, 0x1FFF));
+ Add(new cMenuEditIntItem( tr("Dpid2"), &data.dpids[1], 0, 0x1FFF));
+ Add(new cMenuEditIntItem( tr("Spid1"), &data.spids[0], 0, 0x1FFF));
+ Add(new cMenuEditIntItem( tr("Spid2"), &data.spids[1], 0, 0x1FFF));
+- Add(new cMenuEditIntItem( tr("Tpid"), &data.tpid, 0, 0x1FFF));
++ Add(new cMenuEditIntItem( tr("Tpid"), &data.tpid, 0, 0x1FFF));
+ Add(new cMenuEditCaItem( tr("CA"), &data.caids[0]));
+- Add(new cMenuEditIntItem( tr("Sid"), &data.sid, 1, 0xFFFF));
++ Add(new cMenuEditIntItem( tr("Sid"), &data.sid, 1, 0xFFFF));
+ /* XXX not yet used
+ Add(new cMenuEditIntItem( tr("Nid"), &data.nid, 0));
+ Add(new cMenuEditIntItem( tr("Tid"), &data.tid, 0));
+@@ -280,8 +280,6 @@ void cMenuEditChannel::Setup(void)
+ #ifdef USE_PLUGINPARAM
+ ST("P ") Add(new cMenuEditStrItem( tr("Parameters"), pluginParam, sizeof(pluginParam), tr(FileNameChars)));
+ #endif /* PLUGINPARAM */
+- ST(" T") Add(new cMenuEditMapItem( tr("Alpha"), &data.alpha, AlphaValues));
+- ST(" T") Add(new cMenuEditMapItem( tr("Priority"), &data.priority, PriorityValues));
+ ST(" S ") Add(new cMenuEditMapItem( tr("Rolloff"), &data.rollOff, RollOffValues));
+
+ SetCurrent(Get(current));
+@@ -3341,9 +3339,6 @@ void cMenuSetupDVB::Setup(void)
+ 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();
+@@ -3849,9 +3844,12 @@ void cMenuSetupMisc::Setup(void)
+ #endif /* VOLCTRL */
+ Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Emergency exit"), &data.EmergencyExit));
+ #ifdef USE_LIRCSETTINGS
++ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc push freq"), &data.LircPushFreq, 0, 100));
+ 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 freq"), &data.LircRepeatFreq, 0, 100));
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc repeat timeout"), &data.LircRepeatTimeout, 0, 5000));
++ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc reconnect delay"), &data.LircReconnectDelay, 0, 9999));
++ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc priority boost"), &data.LircPriorityBoost, 0, 100));
+ #endif /* LIRCSETTINGS */
+ }
+
+@@ -5891,7 +5889,7 @@ bool cReplayControl::ShowProgress(bool I
+ lastCurrent = lastTotal = -1;
+ }
+ if (Total != lastTotal) {
+- displayReplay->SetTotal(IndexToHMSF(Total));
++ displayReplay->SetTotal(IndexToHMSF(Total, false, GetFramesPerSec()));
+ if (!Initial)
+ displayReplay->Flush();
+ }
+@@ -5899,7 +5897,7 @@ bool cReplayControl::ShowProgress(bool I
+ displayReplay->SetProgress(Current, Total);
+ if (!Initial)
+ displayReplay->Flush();
+- displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames));
++ displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames, GetFramesPerSec()));
+ displayReplay->Flush();
+ lastCurrent = Current;
+ }
+@@ -5932,8 +5930,8 @@ void cReplayControl::TimeSearchProcess(e
+ {
+ #define STAY_SECONDS_OFF_END 10
+ int Seconds = (timeSearchTime >> 24) * 36000 + ((timeSearchTime & 0x00FF0000) >> 16) * 3600 + ((timeSearchTime & 0x0000FF00) >> 8) * 600 + (timeSearchTime & 0x000000FF) * 60;
+- int Current = (lastCurrent / FRAMESPERSEC);
+- int Total = (lastTotal / FRAMESPERSEC);
++ int Current = (lastCurrent / GetFramesPerSec());
++ int Total = (lastTotal / GetFramesPerSec());
+ switch (Key) {
+ case k0 ... k9:
+ if (timeSearchPos < 4) {
+@@ -5960,7 +5958,7 @@ void cReplayControl::TimeSearchProcess(e
+ case kDown:
+ case kOk:
+ Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds);
+- Goto(Seconds * FRAMESPERSEC, Key == kDown || Key == kPause || Key == kOk);
++ Goto(Seconds * GetFramesPerSec(), Key == kDown || Key == kPause || Key == kOk);
+ timeSearchActive = false;
+ break;
+ default:
+@@ -6139,7 +6137,7 @@ void cReplayControl::EditTest(void)
+ #endif /* JUMPPLAY */
+ m = marks.Next(m);
+ if (m) {
+- Goto(m->position - SecondsToFrames(3));
++ Goto(m->position - SecondsToFrames(3, GetFramesPerSec()));
+ Play();
+ }
+ }
+diff -ruNp vdr-1.7.0-extensions/nit.c vdr-1.7.0-ext-h264-s2ng-speedup/nit.c
+--- vdr-1.7.0-extensions/nit.c 2008-04-12 14:06:40.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/nit.c 2009-04-12 23:52:16.000000000 +0200
+@@ -4,7 +4,7 @@
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+- * $Id: nit.c 2.1 2008/04/12 12:06:40 kls Exp $
++ * $Id: nit.c 2.3 2008/12/20 10:57:50 kls Exp $
+ */
+
+ #include "nit.h"
+@@ -127,13 +127,13 @@ void cNitFilter::Process(u_short Pid, u_
+ int Frequency = Frequencies[0] = BCD2INT(sd->getFrequency()) / 100;
+ static char Polarizations[] = { 'h', 'v', 'l', 'r' };
+ char Polarization = Polarizations[sd->getPolarization()];
+- static int CodeRates[] = { DVBFE_FEC_NONE, DVBFE_FEC_1_2, DVBFE_FEC_2_3, DVBFE_FEC_3_4, DVBFE_FEC_5_6, DVBFE_FEC_7_8, DVBFE_FEC_8_9, DVBFE_FEC_3_5, DVBFE_FEC_4_5, DVBFE_FEC_9_10, DVBFE_FEC_AUTO, DVBFE_FEC_AUTO, DVBFE_FEC_AUTO, DVBFE_FEC_AUTO, DVBFE_FEC_AUTO, DVBFE_FEC_NONE };
++ static int CodeRates[] = { FEC_NONE, FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_8_9, FEC_3_5, FEC_4_5, FEC_9_10, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_NONE };
+ int CodeRate = CodeRates[sd->getFecInner()];
+- static int Modulations[] = { DVBFE_MOD_AUTO, DVBFE_MOD_QPSK, DVBFE_MOD_8PSK, DVBFE_MOD_QAM16 };
++ static int Modulations[] = { QAM_AUTO, QPSK, PSK_8, QAM_16 };
+ int Modulation = Modulations[sd->getModulationType()];
+- int System = sd->getModulationSystem() ? DVBFE_DELSYS_DVBS2 : DVBFE_DELSYS_DVBS;
+- static int RollOffs[] = { DVBFE_ROLLOFF_35, DVBFE_ROLLOFF_25, DVBFE_ROLLOFF_20, DVBFE_ROLLOFF_UNKNOWN };
+- int RollOff = sd->getModulationSystem() ? RollOffs[sd->getRollOff()] : DVBFE_ROLLOFF_UNKNOWN;
++ int System = sd->getModulationSystem() ? SYS_DVBS2 : SYS_DVBS;
++ static int RollOffs[] = { ROLLOFF_35, ROLLOFF_25, ROLLOFF_20, ROLLOFF_AUTO };
++ int RollOff = sd->getModulationSystem() ? RollOffs[sd->getRollOff()] : ROLLOFF_AUTO;
+ int SymbolRate = BCD2INT(sd->getSymbolRate()) / 10;
+ if (ThisNIT >= 0) {
+ for (int n = 0; n < NumFrequencies; n++) {
+@@ -147,21 +147,20 @@ void cNitFilter::Process(u_short Pid, u_
+ }
+ if (Setup.UpdateChannels >= 5) {
+ bool found = false;
+- for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
+- if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
+- int transponder = Channel->Transponder();
+- found = true;
+- if (!ISTRANSPONDER(cChannel::Transponder(Frequency, Polarization), transponder)) {
+- for (int n = 0; n < NumFrequencies; n++) {
+- if (ISTRANSPONDER(cChannel::Transponder(Frequencies[n], Polarization), transponder)) {
+- Frequency = Frequencies[n];
+- break;
+- }
++ cIterator<cChannel> ChannelIterator = Channels.GetChannelsBySourceNidTid(Source, ts.getOriginalNetworkId(), ts.getTransportStreamId());
++ for (cChannel *Channel = ChannelIterator.First(); Channel; Channel = ChannelIterator.Next()) {
++ int transponder = Channel->Transponder();
++ found = true;
++ if (!ISTRANSPONDER(cChannel::Transponder(Frequency, Polarization), transponder)) {
++ for (int n = 0; n < NumFrequencies; n++) {
++ if (ISTRANSPONDER(cChannel::Transponder(Frequencies[n], Polarization), transponder)) {
++ Frequency = Frequencies[n];
++ break;
+ }
+- }
+- if (ISTRANSPONDER(cChannel::Transponder(Frequency, Polarization), Transponder())) // only modify channels if we're actually receiving this transponder
+- Channel->SetSatTransponderData(Source, Frequency, Polarization, SymbolRate, CodeRate, Modulation, System, RollOff);
++ }
+ }
++ if (ISTRANSPONDER(cChannel::Transponder(Frequency, Polarization), Transponder())) // only modify channels if we're actually receiving this transponder
++ Channel->SetSatTransponderData(Source, Frequency, Polarization, SymbolRate, CodeRate, Modulation, System, RollOff);
+ }
+ if (!found) {
+ for (int n = 0; n < NumFrequencies; n++) {
+@@ -181,9 +180,9 @@ void cNitFilter::Process(u_short Pid, u_
+ int Source = cSource::FromData(cSource::stCable);
+ int Frequency = Frequencies[0] = BCD2INT(sd->getFrequency()) / 10;
+ //XXX FEC_outer???
+- static int CodeRates[] = { DVBFE_FEC_NONE, DVBFE_FEC_1_2, DVBFE_FEC_2_3, DVBFE_FEC_3_4, DVBFE_FEC_5_6, DVBFE_FEC_7_8, DVBFE_FEC_8_9, DVBFE_FEC_3_5, DVBFE_FEC_4_5, DVBFE_FEC_9_10, DVBFE_FEC_AUTO, DVBFE_FEC_AUTO, DVBFE_FEC_AUTO, DVBFE_FEC_AUTO, DVBFE_FEC_AUTO, DVBFE_FEC_NONE };
++ static int CodeRates[] = { FEC_NONE, FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_8_9, FEC_3_5, FEC_4_5, FEC_9_10, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_NONE };
+ int CodeRate = CodeRates[sd->getFecInner()];
+- static int Modulations[] = { DVBFE_MOD_NONE, DVBFE_MOD_QAM16, DVBFE_MOD_QAM32, DVBFE_MOD_QAM64, DVBFE_MOD_QAM128, DVBFE_MOD_QAM256, QAM_AUTO };
++ static int Modulations[] = { QPSK, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256, QAM_AUTO };
+ int Modulation = Modulations[min(sd->getModulation(), 6)];
+ int SymbolRate = BCD2INT(sd->getSymbolRate()) / 10;
+ if (ThisNIT >= 0) {
+@@ -198,21 +197,20 @@ void cNitFilter::Process(u_short Pid, u_
+ }
+ if (Setup.UpdateChannels >= 5) {
+ bool found = false;
+- for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
+- if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
+- int transponder = Channel->Transponder();
+- found = true;
+- if (!ISTRANSPONDER(Frequency / 1000, transponder)) {
+- for (int n = 0; n < NumFrequencies; n++) {
+- if (ISTRANSPONDER(Frequencies[n] / 1000, transponder)) {
+- Frequency = Frequencies[n];
+- break;
+- }
++ cIterator<cChannel> ChannelIterator = Channels.GetChannelsBySourceNidTid(Source, ts.getOriginalNetworkId(), ts.getTransportStreamId());
++ for (cChannel *Channel = ChannelIterator.First(); Channel; Channel = ChannelIterator.Next()) {
++ int transponder = Channel->Transponder();
++ found = true;
++ if (!ISTRANSPONDER(Frequency / 1000, transponder)) {
++ for (int n = 0; n < NumFrequencies; n++) {
++ if (ISTRANSPONDER(Frequencies[n] / 1000, transponder)) {
++ Frequency = Frequencies[n];
++ break;
+ }
+- }
+- if (ISTRANSPONDER(Frequency / 1000, Transponder())) // only modify channels if we're actually receiving this transponder
+- Channel->SetCableTransponderData(Source, Frequency, Modulation, SymbolRate, CodeRate);
++ }
+ }
++ if (ISTRANSPONDER(Frequency / 1000, Transponder())) // only modify channels if we're actually receiving this transponder
++ Channel->SetCableTransponderData(Source, Frequency, Modulation, SymbolRate, CodeRate);
+ }
+ if (!found) {
+ for (int n = 0; n < NumFrequencies; n++) {
+@@ -231,22 +229,19 @@ void cNitFilter::Process(u_short Pid, u_
+ SI::TerrestrialDeliverySystemDescriptor *sd = (SI::TerrestrialDeliverySystemDescriptor *)d;
+ int Source = cSource::FromData(cSource::stTerr);
+ int Frequency = Frequencies[0] = sd->getFrequency() * 10;
+- static int Bandwidths[] = { DVBFE_BANDWIDTH_8_MHZ, DVBFE_BANDWIDTH_7_MHZ, DVBFE_BANDWIDTH_6_MHZ, DVBFE_BANDWIDTH_5_MHZ, DVBFE_BANDWIDTH_AUTO, DVBFE_BANDWIDTH_AUTO, DVBFE_BANDWIDTH_AUTO, DVBFE_BANDWIDTH_AUTO };
++ static int Bandwidths[] = { 8000000, 7000000, 6000000, 0, 0, 0, 0, 0 };
+ int Bandwidth = Bandwidths[sd->getBandwidth()];
+- static int Constellations[] = { DVBFE_MOD_QPSK, DVBFE_MOD_QAM16, DVBFE_MOD_QAM64, DVBFE_MOD_AUTO };
++ static int Constellations[] = { QPSK, QAM_16, QAM_64, QAM_AUTO };
+ int Constellation = Constellations[sd->getConstellation()];
+- static int CodeRates[] = { DVBFE_FEC_1_2, DVBFE_FEC_2_3, DVBFE_FEC_3_4, DVBFE_FEC_5_6, DVBFE_FEC_7_8, DVBFE_FEC_AUTO, DVBFE_FEC_AUTO, DVBFE_FEC_AUTO };
++ static int Hierarchies[] = { HIERARCHY_NONE, HIERARCHY_1, HIERARCHY_2, HIERARCHY_4, HIERARCHY_AUTO, HIERARCHY_AUTO, HIERARCHY_AUTO, HIERARCHY_AUTO };
++ int Hierarchy = Hierarchies[sd->getHierarchy()];
++ static int CodeRates[] = { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_AUTO, FEC_AUTO, FEC_AUTO };
+ int CodeRateHP = CodeRates[sd->getCodeRateHP()];
+ int CodeRateLP = CodeRates[sd->getCodeRateLP()];
+- static int GuardIntervals[] = { DVBFE_GUARD_INTERVAL_1_32, DVBFE_GUARD_INTERVAL_1_16, DVBFE_GUARD_INTERVAL_1_8, DVBFE_GUARD_INTERVAL_1_4 };
++ static int GuardIntervals[] = { GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4 };
+ int GuardInterval = GuardIntervals[sd->getGuardInterval()];
+- static int TransmissionModes[] = { DVBFE_TRANSMISSION_MODE_2K, DVBFE_TRANSMISSION_MODE_8K, DVBFE_TRANSMISSION_MODE_4K, DVBFE_TRANSMISSION_MODE_AUTO };
++ static int TransmissionModes[] = { TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_AUTO, TRANSMISSION_MODE_AUTO };
+ int TransmissionMode = TransmissionModes[sd->getTransmissionMode()];
+- static int Priorities[] = { DVBFE_STREAM_PRIORITY_LP, DVBFE_STREAM_PRIORITY_HP };
+- int Priority = Priorities[sd->getPriority()];
+- static int Alphas[] = { 0, DVBFE_ALPHA_1, DVBFE_ALPHA_2, DVBFE_ALPHA_4 };
+- int Alpha = Alphas[sd->getHierarchy() & 3];
+- int Hierarchy = Alpha ? DVBFE_HIERARCHY_ON : DVBFE_HIERARCHY_OFF;
+ if (ThisNIT >= 0) {
+ for (int n = 0; n < NumFrequencies; n++) {
+ if (ISTRANSPONDER(Frequencies[n] / 1000000, Transponder())) {
+@@ -259,32 +254,31 @@ void cNitFilter::Process(u_short Pid, u_
+ }
+ if (Setup.UpdateChannels >= 5) {
+ bool found = false;
+- for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
+- if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
+- int transponder = Channel->Transponder();
+- found = true;
+- if (!ISTRANSPONDER(Frequency / 1000000, transponder)) {
+- for (int n = 0; n < NumFrequencies; n++) {
+- if (ISTRANSPONDER(Frequencies[n] / 1000000, transponder)) {
+- Frequency = Frequencies[n];
+- break;
+- }
++ cIterator<cChannel> ChannelIterator = Channels.GetChannelsBySourceNidTid(Source, ts.getOriginalNetworkId(), ts.getTransportStreamId());
++ for (cChannel *Channel = ChannelIterator.First(); Channel; Channel = ChannelIterator.Next()) {
++ int transponder = Channel->Transponder();
++ found = true;
++ if (!ISTRANSPONDER(Frequency / 1000000, transponder)) {
++ for (int n = 0; n < NumFrequencies; n++) {
++ if (ISTRANSPONDER(Frequencies[n] / 1000000, transponder)) {
++ Frequency = Frequencies[n];
++ break;
+ }
+- }
+- if (ISTRANSPONDER(Frequency / 1000000, Transponder())) // only modify channels if we're actually receiving this transponder
+- Channel->SetTerrTransponderData(Source, Frequency, Bandwidth, Constellation, Hierarchy, CodeRateHP, CodeRateLP, GuardInterval, TransmissionMode, Alpha, Priority);
++ }
+ }
++ if (ISTRANSPONDER(Frequency / 1000000, Transponder())) // only modify channels if we're actually receiving this transponder
++ Channel->SetTerrTransponderData(Source, Frequency, Bandwidth, Constellation, Hierarchy, CodeRateHP, CodeRateLP, GuardInterval, TransmissionMode);
+ }
+ if (!found) {
+ for (int n = 0; n < NumFrequencies; n++) {
+ cChannel *Channel = new cChannel;
+ Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0);
+- if (Channel->SetTerrTransponderData(Source, Frequencies[n], Bandwidth, Constellation, Hierarchy, CodeRateHP, CodeRateLP, GuardInterval, TransmissionMode, Alpha, Priority))
++ if (Channel->SetTerrTransponderData(Source, Frequencies[n], Bandwidth, Constellation, Hierarchy, CodeRateHP, CodeRateLP, GuardInterval, TransmissionMode))
+ EITScanner.AddTransponder(Channel);
+ else
+ delete Channel;
+ }
+- }
++ }
+ }
+ }
+ break;
+diff -ruNp vdr-1.7.0-extensions/osd.c vdr-1.7.0-ext-h264-s2ng-speedup/osd.c
+--- vdr-1.7.0-extensions/osd.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/osd.c 2009-04-12 23:57:23.000000000 +0200
+@@ -220,6 +220,8 @@ bool cBitmap::Contains(int x, int y) con
+
+ bool cBitmap::Covers(int x1, int y1, int x2, int y2) const
+ {
++ if (x1 > x2 || y1 > y2) // sanity check
++ return false;
+ x1 -= x0;
+ y1 -= y0;
+ x2 -= x0;
+@@ -229,6 +231,8 @@ bool cBitmap::Covers(int x1, int y1, int
+
+ bool cBitmap::Intersects(int x1, int y1, int x2, int y2) const
+ {
++ if (x1 > x2 || y1 > y2) // sanity check
++ return false;
+ x1 -= x0;
+ y1 -= y0;
+ x2 -= x0;
+@@ -397,15 +401,20 @@ bool cBitmap::SetXpm(const char *const X
+ void cBitmap::SetIndex(int x, int y, tIndex Index)
+ {
+ if (bitmap) {
+- if (0 <= x && x < width && 0 <= y && y < height) {
+- if (bitmap[width * y + x] != Index) {
+- bitmap[width * y + x] = Index;
+- if (dirtyX1 > x) dirtyX1 = x;
+- if (dirtyY1 > y) dirtyY1 = y;
+- if (dirtyX2 < x) dirtyX2 = x;
+- if (dirtyY2 < y) dirtyY2 = y;
+- }
+- }
++ if (0 <= x && x < width && 0 <= y && y < height)
++ SetIndexInternal(x, y, Index);
++ }
++}
++
++void cBitmap::SetIndexInternal(int x, int y, tIndex Index)
++{
++ // this function relies on existing bitmap and valid coordinates
++ if (bitmap[width * y + x] != Index) {
++ bitmap[width * y + x] = Index;
++ if (dirtyX1 > x) dirtyX1 = x;
++ if (dirtyY1 > y) dirtyY1 = y;
++ if (dirtyX2 < x) dirtyX2 = x;
++ if (dirtyY2 < y) dirtyY2 = y;
+ }
+ }
+
+@@ -413,37 +422,147 @@ void cBitmap::DrawPixel(int x, int y, tC
+ {
+ x -= x0;
+ y -= y0;
+- if (0 <= x && x < width && 0 <= y && y < height)
+- SetIndex(x, y, Index(Color));
++ if (bitmap && 0 <= x && x < width && 0 <= y && y < height)
++ SetIndexInternal(x, y, Index(Color));
+ }
+
+ void cBitmap::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
+ {
+ if (bitmap && Bitmap.bitmap && Intersects(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1)) {
+- if (Covers(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1))
++ bool Covered = Covers(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1);
++ if (Covered)
+ Reset();
+ x -= x0;
+ y -= y0;
+- if (ReplacePalette && Covers(x + x0, y + y0, x + x0 + Bitmap.Width() - 1, y + y0 + Bitmap.Height() - 1)) {
++ // determine valid destination area [x1,x2]x[y1,y2] to avoid range checks inside the loops
++ int x1 = max(0, x), x2 = min(0 + width , x + Bitmap.width) - 1;
++ int y1 = max(0, y), y2 = min(0 + height, y + Bitmap.height) - 1;
++
++#define FOR_Y_LOOP0 \
++ tIndex *pRowSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x1 - x)]; \
++ tIndex *pRowDst = &bitmap[width * y1 + x1]; \
++ for (int &yy = y1, ye = min(y2, dirtyY1 - 1); yy <= ye; yy++, pRowDst += width, pRowSrc += Bitmap.width)
++
++#define FOR_Y_LOOP1 \
++ tIndex *pRowSrc = &Bitmap.bitmap[Bitmap.width * (y2 - y) + (x1 - x)]; \
++ tIndex *pRowDst = &bitmap[width * y2 + x1]; \
++ for (int &yy = y2, ye = max(y1, dirtyY2 + 1); yy >= ye; yy--, pRowDst -= width, pRowSrc -= Bitmap.width)
++
++#define DETECT_DIRTY_AREA_Y(Reverse, TransferCondition, TransferOperation) \
++ do { \
++ FOR_Y_LOOP##Reverse { \
++ tIndex *pSrc = pRowSrc; \
++ tIndex *pDst = pRowDst; \
++ bool GotDirty = false; \
++ for (int xx = x1; xx <= x2; xx++) { \
++ if (TransferCondition) { \
++ if (*pDst != TransferOperation) { \
++ GotDirty = true; \
++ if (dirtyX1 > xx) dirtyX1 = xx; \
++ if (dirtyX2 < xx) dirtyX2 = xx; \
++ } \
++ } \
++ pSrc++; \
++ pDst++; \
++ } \
++ if (GotDirty) { \
++ if (dirtyY1 > yy) dirtyY1 = yy; \
++ if (dirtyY2 < yy) dirtyY2 = yy; \
++ break; \
++ } \
++ } \
++ } \
++ while (false)
++
++#define FOR_X_LOOP0 \
++ tIndex *pColSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x1 - x)]; \
++ tIndex *pColDst = &bitmap[width * y1 + x1]; \
++ for (int &xx = x1, xe = min(x2, dirtyX1 - 1); xx <= xe; xx++, pColDst++, pColSrc++)
++
++#define FOR_X_LOOP1 \
++ tIndex *pColSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x2 - x)]; \
++ tIndex *pColDst = &bitmap[width * y1 + x2]; \
++ for (int &xx = x2, xe = max(x1, dirtyX2 + 1); xx >= xe; xx--, pColDst--, pColSrc--)
++
++#define DETECT_DIRTY_AREA_X(Reverse, TransferCondition, TransferOperation) \
++ do { \
++ FOR_X_LOOP##Reverse { \
++ tIndex *pSrc = pColSrc; \
++ tIndex *pDst = pColDst; \
++ bool GotDirty = false; \
++ for (int yy = y1; yy <= y2; yy++) { \
++ if (TransferCondition) { \
++ if (*pDst != TransferOperation) { \
++ GotDirty = true; \
++ if (dirtyX1 > xx) dirtyX1 = xx; \
++ if (dirtyX2 < xx) dirtyX2 = xx; \
++ break; \
++ } \
++ } \
++ pSrc += Bitmap.width; \
++ pDst += width; \
++ } \
++ if (GotDirty) \
++ break; \
++ } \
++ } \
++ while (false)
++
++#define DRAW_BITMAP(TransferCondition, TransferOperation, CanUseMemCpy) \
++ do { \
++ DETECT_DIRTY_AREA_Y(0, TransferCondition, TransferOperation); /* above */ \
++ DETECT_DIRTY_AREA_Y(1, TransferCondition, TransferOperation); /* below */ \
++ if (y2 < y1) /* nothing dirty */ \
++ return; \
++ DETECT_DIRTY_AREA_X(0, TransferCondition, TransferOperation); /* left */ \
++ DETECT_DIRTY_AREA_X(1, TransferCondition, TransferOperation); /* right */ \
++ /* process dirty area now */ \
++ tIndex *pRowSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x1 - x)]; \
++ tIndex *pRowDst = &bitmap[width * y1 + x1]; \
++ int n = sizeof(tIndex) * (x2 - x1 + 1); \
++ for (int yy = y1; yy <= y2; yy++) { \
++ tIndex *pSrc = pRowSrc; \
++ tIndex *pDst = pRowDst; \
++ if (CanUseMemCpy) \
++ memcpy(pDst, pSrc, n); \
++ else { \
++ for (int xx = x1; xx <= x2; xx++) { \
++ if (TransferCondition) \
++ *pDst = TransferOperation; \
++ pSrc++; \
++ pDst++; \
++ } \
++ } \
++ pRowSrc += Bitmap.width; \
++ pRowDst += width; \
++ } \
++ } \
++ while (false)
++
++ if (ReplacePalette && Covered) {
+ Replace(Bitmap);
+- for (int ix = 0; ix < Bitmap.width; ix++) {
+- for (int iy = 0; iy < Bitmap.height; iy++) {
+- if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
+- SetIndex(x + ix, y + iy, Bitmap.bitmap[Bitmap.width * iy + ix]);
+- }
+- }
++ if (Overlay)
++ DRAW_BITMAP(*pSrc != 0, *pSrc, false);
++ else
++ DRAW_BITMAP(true, *pSrc, true);
+ }
+ else {
+ tIndexes Indexes;
+ Take(Bitmap, &Indexes, ColorFg, ColorBg);
+- for (int ix = 0; ix < Bitmap.width; ix++) {
+- for (int iy = 0; iy < Bitmap.height; iy++) {
+- if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
+- SetIndex(x + ix, y + iy, Indexes[int(Bitmap.bitmap[Bitmap.width * iy + ix])]);
+- }
+- }
++ if (Overlay)
++ DRAW_BITMAP(*pSrc != 0, Indexes[(int)*pSrc], false);
++ else
++ DRAW_BITMAP(true, Indexes[(int)*pSrc], false);
+ }
+ }
++
++#undef DRAW_BITMAP
++#undef DETECT_DIRTY_AREA_Y
++#undef FOR_Y_LOOP0
++#undef FOR_Y_LOOP1
++#undef DETECT_DIRTY_AREA_X
++#undef FOR_X_LOOP0
++#undef FOR_X_LOOP1
+ }
+
+ void cBitmap::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
+@@ -505,10 +624,91 @@ void cBitmap::DrawRectangle(int x1, int
+ x2 = min(x2, width - 1);
+ y2 = min(y2, height - 1);
+ tIndex c = Index(Color);
+- for (int y = y1; y <= y2; y++)
+- for (int x = x1; x <= x2; x++)
+- SetIndex(x, y, c);
++
++#define FOR_Y_LOOP0 \
++ tIndex *pRowDst = &bitmap[width * y1 + x1]; \
++ for (int &yy = y1, ye = min(y2, dirtyY1 - 1); yy <= ye; yy++, pRowDst += width)
++
++#define FOR_Y_LOOP1 \
++ tIndex *pRowDst = &bitmap[width * y2 + x1]; \
++ for (int &yy = y2, ye = max(y1, dirtyY2 + 1); yy >= ye; yy--, pRowDst -= width)
++
++#define DETECT_DIRTY_AREA_Y(Reverse) \
++ do { \
++ FOR_Y_LOOP##Reverse { \
++ tIndex *pDst = pRowDst; \
++ bool GotDirty = false; \
++ for (int xx = x1; xx <= x2; xx++) { \
++ if (*pDst != c) { \
++ GotDirty = true; \
++ if (dirtyX1 > xx) dirtyX1 = xx; \
++ if (dirtyX2 < xx) dirtyX2 = xx; \
++ } \
++ pDst++; \
++ } \
++ if (GotDirty) { \
++ if (dirtyY1 > yy) dirtyY1 = yy; \
++ if (dirtyY2 < yy) dirtyY2 = yy; \
++ break; \
++ } \
++ } \
++ } \
++ while (false)
++
++#define FOR_X_LOOP0 \
++ tIndex *pColDst = &bitmap[width * y1 + x1]; \
++ for (int &xx = x1, xe = min(x2, dirtyX1 - 1); xx <= xe; xx++, pColDst++)
++
++#define FOR_X_LOOP1 \
++ tIndex *pColDst = &bitmap[width * y1 + x2]; \
++ for (int &xx = x2, xe = max(x1, dirtyX2 + 1); xx >= xe; xx--, pColDst--)
++
++#define DETECT_DIRTY_AREA_X(Reverse) \
++ do { \
++ FOR_X_LOOP##Reverse { \
++ tIndex *pDst = pColDst; \
++ bool GotDirty = false; \
++ for (int yy = y1; yy <= y2; yy++) { \
++ if (*pDst != c) { \
++ GotDirty = true; \
++ if (dirtyX1 > xx) dirtyX1 = xx; \
++ if (dirtyX2 < xx) dirtyX2 = xx; \
++ break; \
++ } \
++ pDst += width; \
++ } \
++ if (GotDirty) \
++ break; \
++ } \
++ } \
++ while (false)
++
++ DETECT_DIRTY_AREA_Y(0); /* above */
++ DETECT_DIRTY_AREA_Y(1); /* below */
++ if (y2 < y1) /* nothing dirty */
++ return;
++ DETECT_DIRTY_AREA_X(0); /* left */
++ DETECT_DIRTY_AREA_X(1); /* right */
++ // now fill only dirty area of rectangle
++ tIndex *pRowDst = &bitmap[width * y1 + x1];
++ tIndex *pDst = pRowDst;
++ for (int x = x1; x <= x2; x++)
++ *pDst++ = c;
++ // copy the single line above to all other lines
++ tIndex *pRowSrc = pRowDst;
++ int n = sizeof(tIndex) * (x2 - x1 + 1);
++ for (int y = y1 + 1; y <= y2; y++) {
++ pRowDst += width;
++ memcpy(pRowDst, pRowSrc, n);
++ }
+ }
++
++#undef DETECT_DIRTY_AREA_Y
++#undef FOR_Y_LOOP0
++#undef FOR_Y_LOOP1
++#undef DETECT_DIRTY_AREA_X
++#undef FOR_X_LOOP0
++#undef FOR_X_LOOP1
+ }
+
+ void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)
+diff -ruNp vdr-1.7.0-extensions/osd.h vdr-1.7.0-ext-h264-s2ng-speedup/osd.h
+--- vdr-1.7.0-extensions/osd.h 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/osd.h 2009-04-12 23:57:23.000000000 +0200
+@@ -135,6 +135,7 @@ private:
+ int x0, y0;
+ int width, height;
+ int dirtyX1, dirtyY1, dirtyX2, dirtyY2;
++ void SetIndexInternal(int x, int y, tIndex Index);
+ public:
+ cBitmap(int Width, int Height, int Bpp, int X0 = 0, int Y0 = 0);
+ ///< Creates a bitmap with the given Width, Height and color depth (Bpp).
+diff -ruNp vdr-1.7.0-extensions/pat.c vdr-1.7.0-ext-h264-s2ng-speedup/pat.c
+--- vdr-1.7.0-extensions/pat.c 2008-04-12 15:34:50.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/pat.c 2008-07-06 16:01:32.000000000 +0200
+@@ -4,7 +4,7 @@
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+- * $Id: pat.c 2.1 2008/04/12 13:34:50 kls Exp $
++ * $Id: pat.c 2.2 2008/07/06 14:01:32 kls Exp $
+ */
+
+ #include "pat.h"
+@@ -328,7 +328,8 @@ void cPatFilter::Process(u_short Pid, u_
+ // Scan the stream-specific loop:
+ SI::PMT::Stream stream;
+ int Vpid = 0;
+- int Ppid = pmt.getPCRPid();
++ int Ppid = 0;
++ int Vtype = 0;
+ int Apids[MAXAPIDS + 1] = { 0 }; // these lists are zero-terminated
+ int Dpids[MAXDPIDS + 1] = { 0 };
+ int Spids[MAXSPIDS + 1] = { 0 };
+@@ -343,8 +344,10 @@ void cPatFilter::Process(u_short Pid, u_
+ switch (stream.getStreamType()) {
+ case 1: // STREAMTYPE_11172_VIDEO
+ case 2: // STREAMTYPE_13818_VIDEO
+-//TODO case 0x1B: // MPEG4
++ case 0x1B: // MPEG4
+ Vpid = stream.getPid();
++ Ppid = pmt.getPCRPid();
++ Vtype = stream.getStreamType();
+ break;
+ case 3: // STREAMTYPE_11172_AUDIO
+ case 4: // STREAMTYPE_13818_AUDIO
+@@ -440,7 +443,7 @@ void cPatFilter::Process(u_short Pid, u_
+ }
+ }
+ if (Setup.UpdateChannels >= 2) {
+- Channel->SetPids(Vpid, Vpid ? Ppid : 0, Apids, ALangs, Dpids, DLangs, Spids, SLangs, Tpid);
++ Channel->SetPids(Vpid, Ppid, Vtype, Apids, ALangs, Dpids, DLangs, Spids, SLangs, Tpid);
+ Channel->SetCaIds(CaDescriptors->CaIds());
+ }
+ Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors));
+diff -ruNp vdr-1.7.0-extensions/po/de_DE.po vdr-1.7.0-ext-h264-s2ng-speedup/po/de_DE.po
+--- vdr-1.7.0-extensions/po/de_DE.po 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/po/de_DE.po 2009-04-12 23:57:23.000000000 +0200
+@@ -809,6 +809,9 @@ msgstr "Vor-/Rücklauf mit Rechts/Links"
+ msgid "Setup.Miscellaneous$only in progress display"
+ msgstr "nur in Fortschrittsanzeige"
+
++msgid "Setup.Miscellaneous$Lirc push freq"
++msgstr "Lirc Druck Frequenz"
++
+ msgid "Setup.Miscellaneous$Lirc repeat delay"
+ msgstr "Lirc Verzögerung"
+
+@@ -818,6 +821,12 @@ msgstr "Lirc Frequenz"
+ msgid "Setup.Miscellaneous$Lirc repeat timeout"
+ msgstr "Lirc Zeitbeschränkung"
+
++msgid "Setup.Miscellaneous$Lirc reconnect delay"
++msgstr "Lirc Verbindungs Verzögerung"
++
++msgid "Setup.Miscellaneous$Lirc priority boost"
++msgstr "Lirc Prioritäts Verstärkung"
++
+ msgid "Plugins"
+ msgstr "Plugins"
+
+@@ -1170,9 +1179,6 @@ 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."
+
+diff -ruNp vdr-1.7.0-extensions/recording.c vdr-1.7.0-ext-h264-s2ng-speedup/recording.c
+--- vdr-1.7.0-extensions/recording.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/recording.c 2009-04-12 23:57:23.000000000 +0200
+@@ -2272,11 +2272,11 @@ cUnbufferedFile *cFileName::NextFile(voi
+
+ // --- Index stuff -----------------------------------------------------------
+
+-cString IndexToHMSF(int Index, bool WithFrame)
++cString IndexToHMSF(int Index, bool WithFrame, int FramesPerSec)
+ {
+ char buffer[16];
+- int f = (Index % FRAMESPERSEC) + 1;
+- int s = (Index / FRAMESPERSEC);
++ int f = (Index % FramesPerSec) + 1;
++ int s = (Index / FramesPerSec);
+ int m = s / 60 % 60;
+ int h = s / 3600;
+ s %= 60;
+@@ -2284,17 +2284,17 @@ cString IndexToHMSF(int Index, bool With
+ return buffer;
+ }
+
+-int HMSFToIndex(const char *HMSF)
++int HMSFToIndex(const char *HMSF, int FramesPerSec)
+ {
+ int h, m, s, f = 0;
+ if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
+- return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
++ return (h * 3600 + m * 60 + s) * FramesPerSec + f - 1;
+ return 0;
+ }
+
+-int SecondsToFrames(int Seconds)
++int SecondsToFrames(int Seconds, int FramesPerSec)
+ {
+- return Seconds * FRAMESPERSEC;
++ return Seconds * FramesPerSec;
+ }
+
+ // --- ReadFrame -------------------------------------------------------------
+diff -ruNp vdr-1.7.0-extensions/recording.h vdr-1.7.0-ext-h264-s2ng-speedup/recording.h
+--- vdr-1.7.0-extensions/recording.h 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/recording.h 2009-04-12 23:57:23.000000000 +0200
+@@ -349,11 +349,11 @@ public:
+ cUnbufferedFile *NextFile(void);
+ };
+
+-cString IndexToHMSF(int Index, bool WithFrame = false);
++cString IndexToHMSF(int Index, bool WithFrame = false, int FramesPerSec = FRAMESPERSEC);
+ // Converts the given index to a string, optionally containing the frame number.
+-int HMSFToIndex(const char *HMSF);
++int HMSFToIndex(const char *HMSF, int FramesPerSec = FRAMESPERSEC);
+ // Converts the given string (format: "hh:mm:ss.ff") to an index.
+-int SecondsToFrames(int Seconds); //XXX+ ->player???
++int SecondsToFrames(int Seconds, int FramesPerSec = FRAMESPERSEC); //XXX+ ->player???
+ // Returns the number of frames corresponding to the given number of seconds.
+
+ int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max);
+diff -ruNp vdr-1.7.0-extensions/remux.c vdr-1.7.0-ext-h264-s2ng-speedup/remux.c
+--- vdr-1.7.0-extensions/remux.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/remux.c 2009-04-12 23:57:23.000000000 +0200
+@@ -19,6 +19,8 @@
+ #include "channels.h"
+ #include "shutdown.h"
+ #include "tools.h"
++#include "recording.h"
++#include "h264parser.h"
+
+ ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
+ {
+@@ -100,8 +102,9 @@ protected:
+ int suppressedLogMessages;
+ bool LogAllowed(void);
+ void DroppedData(const char *Reason, int Count) { LOG("%s (dropped %d bytes)", Reason, Count); }
++ virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
+ public:
+- static int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
++ static int PutAllOrNothing(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
+ cRepacker(void);
+ virtual ~cRepacker() {}
+ virtual void Reset(void) { initiallySyncing = true; }
+@@ -138,6 +141,11 @@ bool cRepacker::LogAllowed(void)
+
+ int cRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded)
+ {
++ return PutAllOrNothing(ResultBuffer, Data, Count, CapacityNeeded);
++}
++
++int cRepacker::PutAllOrNothing(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded)
++{
+ if (CapacityNeeded >= Count && ResultBuffer->Free() < CapacityNeeded) {
+ esyslog("ERROR: possible result buffer overflow, dropped %d out of %d byte", CapacityNeeded, CapacityNeeded);
+ return 0;
+@@ -156,7 +164,7 @@ protected:
+ int packetTodo;
+ uchar fragmentData[6 + 65535 + 3];
+ int fragmentLen;
+- uchar pesHeader[6 + 3 + 255 + 3];
++ uchar pesHeader[6 + 3 + 255 + 5 + 3]; // 5: H.264 AUD
+ int pesHeaderLen;
+ uchar pesHeaderBackup[6 + 3 + 255];
+ int pesHeaderBackupLen;
+@@ -164,7 +172,7 @@ protected:
+ uint32_t localScanner;
+ int localStart;
+ bool PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+- virtual int QuerySnoopSize() { return 4; }
++ virtual int QuerySnoopSize(void) { return 4; }
+ virtual void Reset(void);
+ };
+
+@@ -238,8 +246,139 @@ bool cCommonRepacker::PushOutPacket(cRin
+ return true;
+ }
+
++// --- cAudGenerator ---------------------------------------------------------
++
++class cAudGenerator {
++private:
++ H264::cSimpleBuffer buffer;
++ int overflowByteCount;
++ H264::cSliceHeader::eAccessUnitType accessUnitType;
++ int sliceTypes;
++public:
++ cAudGenerator(void);
++ void CollectSliceType(const H264::cSliceHeader *SH);
++ int CollectData(const uchar *Data, int Count);
++ void Generate(cRingBufferLinear *const ResultBuffer);
++};
++
++cAudGenerator::cAudGenerator()
++ : buffer(MAXFRAMESIZE)
++{
++ overflowByteCount = 0;
++ accessUnitType = H264::cSliceHeader::Frame;
++ sliceTypes = 0;
++}
++
++int cAudGenerator::CollectData(const uchar *Data, int Count)
++{
++ // buffer frame data until AUD can be generated
++ int n = buffer.Put(Data, Count);
++ overflowByteCount += (Count - n);
++ // always report "success" as an error message will be shown in Generate()
++ return Count;
++}
++
++void cAudGenerator::CollectSliceType(const H264::cSliceHeader *SH)
++{
++ if (!SH)
++ return;
++ // remember type of current access unit
++ accessUnitType = SH->GetAccessUnitType();
++ // translate slice_type into part of primary_pic_type and merge them
++ switch (SH->slice_type) {
++ case 2: // I
++ case 7: // I only => I
++ sliceTypes |= 0x10000;
++ break;
++ case 0: // P
++ case 5: // P only => I, P
++ sliceTypes |= 0x11000;
++ break;
++ case 1: // B
++ case 6: // B only => I, P, B
++ sliceTypes |= 0x11100;
++ break;
++ case 4: // SI
++ case 9: // SI only => SI
++ sliceTypes |= 0x00010;
++ break;
++ case 3: // SP
++ case 8: // SP only => SI, SP
++ sliceTypes |= 0x00011;
++ break;
++ }
++}
++
++void cAudGenerator::Generate(cRingBufferLinear *const ResultBuffer)
++{
++ int primary_pic_type;
++ // translate the merged primary_pic_type parts into primary_pic_type
++ switch (sliceTypes) {
++ case 0x10000: // I
++ primary_pic_type = 0;
++ break;
++ case 0x11000: // I, P
++ primary_pic_type = 1;
++ break;
++ case 0x11100: // I, P, B
++ primary_pic_type = 2;
++ break;
++ case 0x00010: // SI
++ primary_pic_type = 3;
++ break;
++ case 0x00011: // SI, SP
++ primary_pic_type = 4;
++ break;
++ case 0x10010: // I, SI
++ primary_pic_type = 5;
++ break;
++ case 0x11011: // I, SI, P, SP
++ case 0x10011: // I, SI, SP
++ case 0x11010: // I, SI, P
++ primary_pic_type = 6;
++ break;
++ case 0x11111: // I, SI, P, SP, B
++ case 0x11110: // I, SI, P, B
++ primary_pic_type = 7;
++ break;
++ default:
++ primary_pic_type = -1; // frame without slices?
++ }
++ // drop an incorrect frame
++ if (primary_pic_type < 0)
++ esyslog("ERROR: cAudGenerator::Generate(): dropping frame without slices");
++ else {
++ // drop a partitial frame
++ if (overflowByteCount > 0)
++ esyslog("ERROR: cAudGenerator::Generate(): frame exceeds MAXFRAMESIZE bytes (required size: %d bytes), dropping frame", buffer.Size() + overflowByteCount);
++ else {
++ int Count;
++ uchar *Data = buffer.Get(Count);
++ int PesPayloadOffset = 0;
++ AnalyzePesHeader(Data, Count, PesPayloadOffset);
++ // enter primary_pic_type into AUD
++ Data[ PesPayloadOffset + 4 ] |= primary_pic_type << 5;
++ // mangle the "start code" to pass the information that this access unit is a
++ // bottom field to ScanVideoPacket() where this modification will be reverted.
++ if (accessUnitType == H264::cSliceHeader::BottomField)
++ Data[ PesPayloadOffset + 3 ] |= 0x80;
++ // store the buffered frame
++ cRepacker::PutAllOrNothing(ResultBuffer, Data, Count, Count);
++ }
++ }
++ // prepare for next run
++ buffer.Clear();
++ overflowByteCount = 0;
++ sliceTypes = 0;
++}
++
+ // --- cVideoRepacker --------------------------------------------------------
+
++#define IPACKS 2048
++#define SC_SEQUENCE 0xB3 // "sequence header code"
++#define SC_GROUP 0xB8 // "group start code"
++#define SC_PICTURE 0x00 // "picture start code"
++
+ class cVideoRepacker : public cCommonRepacker {
+ private:
+ enum eState {
+@@ -248,37 +387,243 @@ private:
+ scanPicture
+ };
+ int state;
+- void HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel);
++ bool framePicture;
++ bool pictureExtensionAhead;
++ bool collectChunkData;
++ H264::cSimpleBuffer chunkData;
++ H264::cParser *h264Parser;
++ bool &h264;
++ int sliceSeen;
++ bool audSeen;
++ cAudGenerator *audGenerator;
++ const uchar *startCodeLocations[(IPACKS + 3) / 4];
++ int startCodeLocationCount;
++ int startCodeLocationIndex;
++ bool startCodeLocationsPrepared;
++ void CheckAudGeneration(bool SliceNalUnitType, bool SyncPoint, const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel);
++ void PushOutCurrentFrameAndStartNewPacket(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel);
++ void HandleNalUnit(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel, const uchar *&NalPayload);
++ void HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel, const uchar *&ChunkPayload);
+ inline bool ScanDataForStartCodeSlow(const uchar *const Data);
+ inline bool ScanDataForStartCodeFast(const uchar *&Data, const uchar *Limit);
+- inline bool ScanDataForStartCode(const uchar *&Data, int &Done, int &Todo);
++ inline bool ScanDataForStartCode(const uchar *&Data, int &Done, int &Todo, int PesPayloadOffset);
+ inline void AdjustCounters(const int Delta, int &Done, int &Todo);
+ inline bool ScanForEndOfPictureSlow(const uchar *&Data);
+ inline bool ScanForEndOfPictureFast(const uchar *&Data, const uchar *Limit);
+ inline bool ScanForEndOfPicture(const uchar *&Data, const uchar *Limit);
++ inline void PushStartCodeLocation(const uchar *Data);
++ inline const uchar *PeekStartCodeLocation(void);
++ inline const uchar *PullStartCodeLocation(void);
++ void CollectData(const uchar *Data, int Count);
++ void BeginCollectingPictureExtension(void);
++ void EndCollectingPictureExtension(void);
++ bool DetermineFramePicture(void);
++ void GenerateFieldPicturesHint(bool FramePicture, const uchar *const Data, const uchar AndMask, const uchar OrMask);
++ void SwitchToMpeg12(void);
++protected:
++ virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
+ public:
+- cVideoRepacker(void);
++ cVideoRepacker(bool &H264);
++ ~cVideoRepacker();
+ virtual void Reset(void);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int BreakAt(const uchar *Data, int Count);
+ };
+
+-cVideoRepacker::cVideoRepacker(void)
+-{
++cVideoRepacker::cVideoRepacker(bool &H264)
++: chunkData(1024)
++, h264(H264)
++{
++ // assume H.264 -- we'll fallback to MPEG1/2 when necessary
++ h264 = true;
++ h264Parser = (H264 ? new H264::cParser() : 0);
++ audGenerator = 0;
+ Reset();
+ }
+
++cVideoRepacker::~cVideoRepacker()
++{
++ delete h264Parser;
++ delete audGenerator;
++}
++
+ void cVideoRepacker::Reset(void)
+ {
+ cCommonRepacker::Reset();
++ if (h264Parser)
++ h264Parser->Reset();
+ scanner = 0xFFFFFFFF;
+ state = syncing;
++ framePicture = true;
++ pictureExtensionAhead = false;
++ collectChunkData = false;
++ chunkData.Clear();
++ sliceSeen = -1;
++ audSeen = false;
++ delete audGenerator;
++ audGenerator = 0;
++ startCodeLocationCount = 0;
++ startCodeLocationsPrepared = false;
+ }
+
+-void cVideoRepacker::HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel)
++void cVideoRepacker::SwitchToMpeg12(void)
+ {
+- // synchronisation is detected some bytes after frame start.
+- const int SkippedBytesLimit = 4;
++ if (!h264Parser)
++ return;
++ dsyslog("cVideoRepacker: switching to MPEG1/2 mode");
++ delete h264Parser;
++ h264Parser = 0;
++ delete audGenerator;
++ audGenerator = 0;
++ h264 = false;
++}
++
++int cVideoRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded)
++{
++ if (!audGenerator)
++ return cCommonRepacker::Put(ResultBuffer, Data, Count, CapacityNeeded);
++
++ return audGenerator->CollectData(Data, Count);
++}
++
++void cVideoRepacker::CollectData(const uchar *Data, int Count)
++{
++ if (h264Parser)
++ h264Parser->PutNalUnitData(Data, Count);
++ else if (collectChunkData)
++ chunkData.Put(Data, Count);
++}
++
++void cVideoRepacker::HandleNalUnit(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel, const uchar *&NalPayload)
++{
++ // check whether we need to fall back to MPEG1/2
++ if (initiallySyncing) {
++ switch (*Data) {
++ case SC_SEQUENCE:
++ case SC_GROUP:
++ case SC_PICTURE:
++ // the above start codes do not appear in H.264 so let's switch to MPEG1/2
++ SwitchToMpeg12();
++ // delegate startcode to appropriate handler
++ HandleStartCode(Data, ResultBuffer, Payload, StreamID, MpegLevel, NalPayload);
++ return;
++ }
++ }
++
++ // valid NAL units start with a zero bit
++ if (*Data & 0x80) {
++ LOG("cVideoRepacker: found invalid NAL unit: stream seems to be scrambled or not demultiplexed");
++ return;
++ }
++
++ // collect NAL unit's remaining data and process it
++ CollectData(NalPayload, Data - 3 - NalPayload);
++ h264Parser->Process();
++
++ // which kind of NAL unit have we got?
++ const int nal_unit_type = *Data & 0x1F;
++ switch (nal_unit_type) {
++ case 1: // coded slice of a non-IDR picture
++ case 2: // coded slice data partition A
++ case 5: // coded slice of an IDR picture
++ CheckAudGeneration(true, false, Data, ResultBuffer, Payload, StreamID, MpegLevel);
++ break;
++ case 3: // coded slice data partition B
++ case 4: // coded slice data partition C
++ case 19: // coded slice of an auxiliary coded picture without partitioning
++ break;
++ case 6: // supplemental enhancement information (SEI)
++ case 7: // sequence parameter set
++ case 8: // picture parameter set
++ case 10: // end of sequence
++ case 11: // end of stream
++ case 13: // sequence parameter set extension
++ CheckAudGeneration(false, nal_unit_type == 7, Data, ResultBuffer, Payload, StreamID, MpegLevel);
++ break;
++ case 12: // filler data
++ break;
++ case 14 ... 18: // reserved
++ CheckAudGeneration(false, false, Data, ResultBuffer, Payload, StreamID, MpegLevel);
++ case 20 ... 23: // reserved
++ LOG("cVideoRepacker: found reserved NAL unit type: stream seems to be scrambled");
++ break;
++ case 0: // unspecified
++ case 24 ... 31: // unspecified
++ LOG("cVideoRepacker: found unspecified NAL unit type: stream seems to be scrambled");
++ break;
++ case 9: { // access unit delimiter
++ audSeen = true;
++ CheckAudGeneration(false, true, Data, ResultBuffer, Payload, StreamID, MpegLevel);
++ // mangle the "start code" to pass the information "the next access unit will be the
++ // second field of the current frame" to ScanVideoPacket() where this modification
++ // will be reverted.
++ const H264::cSliceHeader *SH = h264Parser->Context().CurrentSlice();
++ GenerateFieldPicturesHint(!SH || SH->GetAccessUnitType() == H264::cSliceHeader::Frame, Data, 0xFF, 0x80);
++ }
++ break;
++ }
++
++ // collect 0x00 0x00 0x01 for current NAL unit
++ static const uchar InitPayload[3] = { 0x00, 0x00, 0x01 };
++ CollectData(InitPayload, sizeof (InitPayload));
++ NalPayload = Data;
++}
++
++void cVideoRepacker::GenerateFieldPicturesHint(bool FramePicture, const uchar *const Data, const uchar AndMask, const uchar OrMask)
++{
++ if (FramePicture)
++ framePicture = true;
++ else {
++ framePicture ^= true; // toggle between frame/first field and second field
++ if (!framePicture) {
++ // the last picture was a field so set a hint for this second field
++ *(uchar *)Data &= AndMask;
++ *(uchar *)Data |= OrMask;
++ }
++ }
++}
++
++void cVideoRepacker::CheckAudGeneration(bool SliceNalUnitType, bool SyncPoint, const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel)
++{
++ // we cannot generate anything until we have reached the synchronisation point
++ if (sliceSeen < 0 && !SyncPoint)
++ return;
++ // detect transition from slice to non-slice NAL units
++ const bool WasSliceSeen = (sliceSeen != false);
++ const bool IsSliceSeen = SliceNalUnitType;
++ sliceSeen = IsSliceSeen;
++ // collect slice types for AUD generation
++ if (WasSliceSeen && audGenerator)
++ audGenerator->CollectSliceType(h264Parser->Context().CurrentSlice());
++ // handle access unit delimiter at the transition from slice to non-slice NAL units
++ if (WasSliceSeen && !IsSliceSeen) {
++ // an Access Unit Delimiter indicates that the current picture is done. So let's
++ // push out the current frame to start a new packet for the next picture.
++ PushOutCurrentFrameAndStartNewPacket(Data, ResultBuffer, Payload, StreamID, MpegLevel);
++ if (state == findPicture) {
++ // go on with scanning the picture data
++ state++;
++ }
++ // generate the AUD and push out the buffered frame
++ if (audGenerator) {
++ audGenerator->Generate(ResultBuffer);
++ if (audSeen) {
++ // we nolonger need to generate AUDs as they are part of the stream
++ delete audGenerator;
++ audGenerator = 0;
++ }
++ }
++ else if (!audSeen) // we do need to generate AUDs
++ audGenerator = new cAudGenerator;
++ }
++}
++
++void cVideoRepacker::HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel, const uchar *&ChunkPayload)
++{
++ // collect chunk's remaining data
++ CollectData(ChunkPayload, Data - 3 - ChunkPayload);
++ // currently, only picture extension is collected, so end collecting now
++ EndCollectingPictureExtension();
+
+ // which kind of start code have we got?
+ switch (*Data) {
+@@ -292,71 +637,25 @@ void cVideoRepacker::HandleStartCode(con
+ case 0xB4: // sequence error code
+ LOG("cVideoRepacker: found sequence error code: stream seems to be damaged");
+ case 0xB2: // user data start code
++ break;
+ case 0xB5: // extension start code
++ if (pictureExtensionAhead) {
++ pictureExtensionAhead = false;
++ // mangle the "start code" to pass the information "the next access unit will be the
++ // second field of the current frame" to ScanVideoPacket() where this modification
++ // will be reverted.
++ GenerateFieldPicturesHint(DetermineFramePicture(), Data, 0x00, 0xB9);
++ BeginCollectingPictureExtension();
++ }
+ break;
+- case 0xB7: // sequence end code
+- case 0xB3: // sequence header code
+- case 0xB8: // group start code
+ case 0x00: // picture start code
+- if (state == scanPicture) {
+- // the above start codes indicate that the current picture is done. So
+- // push out the packet to start a new packet for the next picuture. If
+- // the byte count get's negative then the current buffer ends in a
+- // partitial start code that must be stripped off, as it shall be put
+- // in the next packet.
+- PushOutPacket(ResultBuffer, Payload, Data - 3 - Payload);
+- // go on with syncing to the next picture
+- state = syncing;
+- }
+- if (state == syncing) {
+- if (initiallySyncing) // omit report for the typical initial case
+- initiallySyncing = false;
+- else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
+- LOG("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - SkippedBytesLimit);
+- skippedBytes = 0;
+- // if there is a PES header available, then use it ...
+- if (pesHeaderBackupLen > 0) {
+- // ISO 13818-1 says:
+- // In the case of video, if a PTS is present in a PES packet header
+- // it shall refer to the access unit containing the first picture start
+- // code that commences in this PES packet. A picture start code commences
+- // in PES packet if the first byte of the picture start code is present
+- // in the PES packet.
+- memcpy(pesHeader, pesHeaderBackup, pesHeaderBackupLen);
+- pesHeaderLen = pesHeaderBackupLen;
+- pesHeaderBackupLen = 0;
+- }
+- else {
+- // ... otherwise create a continuation PES header
+- pesHeaderLen = 0;
+- pesHeader[pesHeaderLen++] = 0x00;
+- pesHeader[pesHeaderLen++] = 0x00;
+- pesHeader[pesHeaderLen++] = 0x01;
+- pesHeader[pesHeaderLen++] = StreamID; // video stream ID
+- pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+- pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+-
+- if (MpegLevel == phMPEG2) {
+- pesHeader[pesHeaderLen++] = 0x80;
+- pesHeader[pesHeaderLen++] = 0x00;
+- pesHeader[pesHeaderLen++] = 0x00;
+- }
+- else
+- pesHeader[pesHeaderLen++] = 0x0F;
+- }
+- // append the first three bytes of the start code
+- pesHeader[pesHeaderLen++] = 0x00;
+- pesHeader[pesHeaderLen++] = 0x00;
+- pesHeader[pesHeaderLen++] = 0x01;
+- // the next packet's payload will begin with the fourth byte of
+- // the start code (= the actual code)
+- Payload = Data;
+- // as there is no length information available, assume the
+- // maximum we can hold in one PES packet
+- packetTodo = maxPacketSize - pesHeaderLen;
+- // go on with finding the picture data
+- state++;
+- }
++ pictureExtensionAhead = true;
++ case 0xB8: // group start code
++ case 0xB3: // sequence header code
++ case 0xB7: // sequence end code
++ // the above start codes indicate that the current picture is done. So let's
++ // push out the current frame to start a new packet for the next picture.
++ PushOutCurrentFrameAndStartNewPacket(Data, ResultBuffer, Payload, StreamID, MpegLevel);
+ break;
+ case 0x01 ... 0xAF: // slice start codes
+ if (state == findPicture) {
+@@ -365,6 +664,116 @@ void cVideoRepacker::HandleStartCode(con
+ }
+ break;
+ }
++
++ // collect 0x00 0x00 0x01 for current chunk
++ static const uchar InitPayload[3] = { 0x00, 0x00, 0x01 };
++ CollectData(InitPayload, sizeof (InitPayload));
++ ChunkPayload = Data;
++}
++
++void cVideoRepacker::BeginCollectingPictureExtension(void)
++{
++ chunkData.Clear();
++ collectChunkData = true;
++}
++
++void cVideoRepacker::EndCollectingPictureExtension(void)
++{
++ collectChunkData = false;
++}
++
++bool cVideoRepacker::DetermineFramePicture(void)
++{
++ bool FieldPicture = false;
++ int Count;
++ uchar *Data = chunkData.Get(Count);
++ if (Data && Count >= 7) {
++ if (!Data[0] && !Data[1] && Data[2] == 0x01 && (Data[3] == 0xB5 || Data[3] == 0xB9)) { // extension startcode or hint
++ if ((Data[4] & 0xF0) == 0x80) { // picture coding extension
++ int picture_structure = Data[6] & 0x03;
++ FieldPicture = picture_structure == 0x01 || picture_structure == 0x02;
++ }
++ }
++ }
++if (FieldPicture) fprintf(stderr, "----- MPEG2 field picture detected ---------------------------\n");
++ return !FieldPicture;
++}
++
++void cVideoRepacker::PushOutCurrentFrameAndStartNewPacket(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel)
++{
++ // synchronisation is detected some bytes after frame start.
++ const int SkippedBytesLimit = 4;
++
++ if (state == scanPicture) {
++ // picture data has been found so let's push out the current frame.
++ // If the byte count get's negative then the current buffer ends in a
++ // partitial start code that must be stripped off, as it shall be put
++ // in the next packet.
++ PushOutPacket(ResultBuffer, Payload, Data - 3 - Payload);
++ // go on with syncing to the next picture
++ state = syncing;
++ }
++ // when already synced to a picture, just go on collecting data
++ if (state != syncing)
++ return;
++ // we're synced to a picture so prepare a new packet
++ if (initiallySyncing) { // omit report for the typical initial case
++ initiallySyncing = false;
++ isyslog("cVideoRepacker: operating in %s mode", h264Parser ? "H.264" : "MPEG1/2");
++ }
++ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
++ LOG("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - SkippedBytesLimit);
++ skippedBytes = 0;
++ // if there is a PES header available, then use it ...
++ if (pesHeaderBackupLen > 0) {
++ // ISO 13818-1 says:
++ // In the case of video, if a PTS is present in a PES packet header
++ // it shall refer to the access unit containing the first picture start
++ // code that commences in this PES packet. A picture start code commences
++ // in PES packet if the first byte of the picture start code is present
++ // in the PES packet.
++ memcpy(pesHeader, pesHeaderBackup, pesHeaderBackupLen);
++ pesHeaderLen = pesHeaderBackupLen;
++ pesHeaderBackupLen = 0;
++ }
++ else {
++ // ... otherwise create a continuation PES header
++ pesHeaderLen = 0;
++ pesHeader[pesHeaderLen++] = 0x00;
++ pesHeader[pesHeaderLen++] = 0x00;
++ pesHeader[pesHeaderLen++] = 0x01;
++ pesHeader[pesHeaderLen++] = StreamID; // video stream ID
++ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
++ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
++
++ if (MpegLevel == phMPEG2) {
++ pesHeader[pesHeaderLen++] = 0x80;
++ pesHeader[pesHeaderLen++] = 0x00;
++ pesHeader[pesHeaderLen++] = 0x00;
++ }
++ else
++ pesHeader[pesHeaderLen++] = 0x0F;
++ }
++ // add an AUD in H.264 mode when not present in stream
++ if (h264Parser && !audSeen) {
++ pesHeader[pesHeaderLen++] = 0x00;
++ pesHeader[pesHeaderLen++] = 0x00;
++ pesHeader[pesHeaderLen++] = 0x01;
++ pesHeader[pesHeaderLen++] = 0x09; // access unit delimiter
++ pesHeader[pesHeaderLen++] = 0x10; // will be filled later
++ }
++ // append the first three bytes of the start code
++ pesHeader[pesHeaderLen++] = 0x00;
++ pesHeader[pesHeaderLen++] = 0x00;
++ pesHeader[pesHeaderLen++] = 0x01;
++ // the next packet's payload will begin with the fourth byte of
++ // the start code (= the actual code)
++ Payload = Data;
++ // as there is no length information available, assume the
++ // maximum we can hold in one PES packet
++ packetTodo = maxPacketSize - pesHeaderLen;
++ // go on with finding the picture data
++ state++;
+ }
+
+ bool cVideoRepacker::ScanDataForStartCodeSlow(const uchar *const Data)
+@@ -377,6 +786,13 @@ bool cVideoRepacker::ScanDataForStartCod
+
+ bool cVideoRepacker::ScanDataForStartCodeFast(const uchar *&Data, const uchar *Limit)
+ {
++ // We enter here when it is safe to access at least 3 bytes before Data (e. g.
++ // the tail of a previous run) and one byte after Data (xx yy zz [aa] bb {cc}).
++ // On return, Data shall either point to the last valid byte of the block or to
++ // the byte qualifying the start code (00 00 01 [ss]).
++ // As we are searching for 0x01, we've to move pointers (xx yy [zz] aa {bb} cc)
++ // to find start code "aa" for example after a packet boundery.
++ Data--;
+ Limit--;
+
+ while (Data < Limit && (Data = (const uchar *)memchr(Data, 0x01, Limit - Data))) {
+@@ -394,29 +810,66 @@ bool cVideoRepacker::ScanDataForStartCod
+ return false;
+ }
+
+-bool cVideoRepacker::ScanDataForStartCode(const uchar *&Data, int &Done, int &Todo)
++bool cVideoRepacker::ScanDataForStartCode(const uchar *&Data, int &Done, int &Todo, int PesPayloadOffset)
+ {
+- const uchar *const DataOrig = Data;
+- const int MinDataSize = 4;
++ const int ReasonableDataSizeForFastScanning = 12;
+
+- if (Todo < MinDataSize || (state != syncing && packetTodo < MinDataSize))
+- return ScanDataForStartCodeSlow(Data);
++ if (!startCodeLocationsPrepared) {
++ // use slow scanning until it is safe to use fast scanning
++ if (Done < PesPayloadOffset + 3)
++ return ScanDataForStartCodeSlow(Data);
++
++ // process available data but not more than needed for the current packet
++ int Limit = Todo;
++ if (state != syncing && Limit > packetTodo)
++ Limit = packetTodo;
++
++ // use slow scanning when there is not enough data left
++ if (Limit < ReasonableDataSizeForFastScanning)
++ return ScanDataForStartCodeSlow(Data);
++
++ // it's reasonable to use fast scanning
++ const uchar *const DataOrig = Data;
++ bool FoundStartCode = ScanDataForStartCodeFast(Data, Data + Limit);
++ AdjustCounters(Data - DataOrig, Done, Todo);
++ return FoundStartCode;
++ }
+
+- int Limit = Todo;
+- if (state != syncing && Limit > packetTodo)
+- Limit = packetTodo;
++ // process available data but not more than needed for the current packet
++ const uchar *Limit = Data + Todo - 1;
++ if (state != syncing && Todo > packetTodo) {
++ if (packetTodo <= 0) // overfill phase
++ Limit = Data; // do a single ScanDataForStartCodeSlow() below
++ else
++ Limit = Data + packetTodo - 1;
++ }
+
+- if (ScanDataForStartCodeSlow(Data))
+- return true;
++ // prepare the "not found" case
++ bool FoundStartCode = false;
++ const uchar *const DataOrig = Data;
++ Data = Limit;
+
+- if (ScanDataForStartCodeSlow(++Data)) {
+- AdjustCounters(1, Done, Todo);
+- return true;
++ // get the next start code location which fits into the limit
++ const uchar *p = PeekStartCodeLocation();
++ if (p && p <= Limit) {
++ Data = PullStartCodeLocation();
++ FoundStartCode = true;
+ }
+- ++Data;
+
+- bool FoundStartCode = ScanDataForStartCodeFast(Data, DataOrig + Limit);
+- AdjustCounters(Data - DataOrig, Done, Todo);
++ // setup the scanner variable
++ int bite = Data - DataOrig;
++ if (bite <= 3) {
++ // to few data, need to do byte shifting
++ for (int i = 0; i <= bite; i++)
++ ScanDataForStartCodeSlow(DataOrig + i);
++ }
++ else {
++ // it's safe to access the last 4 bytes directly
++ uint32_t *Scanner = (uint32_t *)(Data - 3);
++ scanner = ntohl(*Scanner);
++ }
++
++ AdjustCounters(bite, Done, Todo);
+ return FoundStartCode;
+ }
+
+@@ -458,14 +911,19 @@ void cVideoRepacker::Repack(cRingBufferL
+ const uchar *data = Data + done;
+ // remember start of the data
+ const uchar *payload = data;
+-
++ const uchar *ChunkPayload = payload;
++ startCodeLocationIndex = 0;
+ while (todo > 0) {
+ // collect number of skipped bytes while syncing
+ if (state <= syncing)
+ skippedBytes++;
+ // did we reach a start code?
+- if (ScanDataForStartCode(data, done, todo))
+- HandleStartCode(data, ResultBuffer, payload, Data[3], mpegLevel);
++ if (ScanDataForStartCode(data, done, todo, pesPayloadOffset)) {
++ if (h264Parser)
++ HandleNalUnit(data, ResultBuffer, payload, Data[3], mpegLevel, ChunkPayload);
++ else
++ HandleStartCode(data, ResultBuffer, payload, Data[3], mpegLevel, ChunkPayload);
++ }
+ // move on
+ data++;
+ done++;
+@@ -568,31 +1026,75 @@ void cVideoRepacker::Repack(cRingBufferL
+ fragmentLen += bite;
+ }
+ }
++ // collect data as needed
++ CollectData(ChunkPayload, data - ChunkPayload);
+ // report that syncing dropped some bytes
+ if (skippedBytes > SkippedBytesLimit) {
+ if (!initiallySyncing) // omit report for the typical initial case
+ LOG("cVideoRepacker: skipped %d bytes while syncing on next picture", skippedBytes - SkippedBytesLimit);
+ skippedBytes = SkippedBytesLimit;
+ }
++ startCodeLocationCount = 0;
++}
++
++void cVideoRepacker::PushStartCodeLocation(const uchar *Data)
++{
++ startCodeLocations[startCodeLocationCount++] = Data;
++}
++
++const uchar *cVideoRepacker::PeekStartCodeLocation()
++{
++ if (startCodeLocationIndex < startCodeLocationCount)
++ return startCodeLocations[startCodeLocationIndex];
++ return 0;
++}
++
++const uchar *cVideoRepacker::PullStartCodeLocation()
++{
++ const uchar *p = PeekStartCodeLocation();
++ if (p != 0)
++ startCodeLocationIndex++;
++ return p;
+ }
+
+ bool cVideoRepacker::ScanForEndOfPictureSlow(const uchar *&Data)
+ {
+ localScanner <<= 8;
++ if (localScanner != 0x00000100) {
++ localScanner |= *Data++;
++ return false;
++ }
++ PushStartCodeLocation(Data);
+ localScanner |= *Data++;
+ // check start codes which follow picture data
+- switch (localScanner) {
+- case 0x00000100: // picture start code
+- case 0x000001B8: // group start code
+- case 0x000001B3: // sequence header code
+- case 0x000001B7: // sequence end code
+- return true;
+- }
++ if (h264Parser) {
++ int nal_unit_type = localScanner & 0x1F;
++ switch (nal_unit_type) {
++ case 9: // access unit delimiter
++ return true;
++ }
++ }
++ else {
++ switch (localScanner) {
++ case 0x00000100: // picture start code
++ case 0x000001B8: // group start code
++ case 0x000001B3: // sequence header code
++ case 0x000001B7: // sequence end code
++ return true;
++ }
++ }
+ return false;
+ }
+
+ bool cVideoRepacker::ScanForEndOfPictureFast(const uchar *&Data, const uchar *Limit)
+ {
++ // We enter here when it is safe to access at least 3 bytes before Data (e. g.
++ // the tail of a previous run) and one byte after Data (xx yy zz [aa] bb {cc}).
++ // On return, Data shall either point to the first byte outside of the block or
++ // to the byte following the start code (00 00 01 ss [tt]).
++ // As we are searching for 0x01, we've to move pointers (xx yy [zz] aa {bb} cc)
++ // to find start code "aa" for example after a packet boundery.
++ Data--;
+ Limit--;
+
+ while (Data < Limit && (Data = (const uchar *)memchr(Data, 0x01, Limit - Data))) {
+@@ -600,16 +1102,29 @@ bool cVideoRepacker::ScanForEndOfPicture
+ Data += 3;
+ else {
+ localScanner = 0x00000100 | *++Data;
++ PushStartCodeLocation(Data);
+ // check start codes which follow picture data
+- switch (localScanner) {
+- case 0x00000100: // picture start code
+- case 0x000001B8: // group start code
+- case 0x000001B3: // sequence header code
+- case 0x000001B7: // sequence end code
+- Data++;
+- return true;
+- default:
+- Data += 3;
++ if (h264Parser) {
++ int nal_unit_type = localScanner & 0x1F;
++ switch (nal_unit_type) {
++ case 9: // access unit delimiter
++ Data++;
++ return true;
++ default:
++ Data += 3;
++ }
++ }
++ else {
++ switch (localScanner) {
++ case 0x00000100: // picture start code
++ case 0x000001B8: // group start code
++ case 0x000001B3: // sequence header code
++ case 0x000001B7: // sequence end code
++ Data++;
++ return true;
++ default:
++ Data += 3;
++ }
+ }
+ }
+ }
+@@ -622,57 +1137,55 @@ bool cVideoRepacker::ScanForEndOfPicture
+
+ bool cVideoRepacker::ScanForEndOfPicture(const uchar *&Data, const uchar *Limit)
+ {
+- const uchar *const DataOrig = Data;
+- const int MinDataSize = 4;
+- bool FoundEndOfPicture;
++ const int ReasonableDataSizeForFastScanning = 12;
+
+- if (Limit - Data <= MinDataSize) {
+- FoundEndOfPicture = false;
+- while (Data < Limit) {
++ const uchar *const ReasonableLimit = Limit - ReasonableDataSizeForFastScanning;
++ bool FoundEndOfPicture = false;
++
++ while (Data < Limit) {
++ // use slow scanning until it is safe or reasonable to use fast scanning
++ if (localStart < 3 || Data >= ReasonableLimit) {
++ localStart++;
+ if (ScanForEndOfPictureSlow(Data)) {
+ FoundEndOfPicture = true;
+ break;
+ }
+ }
+- }
+- else {
+- FoundEndOfPicture = true;
+- if (!ScanForEndOfPictureSlow(Data)) {
+- if (!ScanForEndOfPictureSlow(Data)) {
+- if (!ScanForEndOfPictureFast(Data, Limit))
+- FoundEndOfPicture = false;
++ else {
++ const uchar *const DataOrig = Data;
++ FoundEndOfPicture = ScanForEndOfPictureFast(Data, Limit);
++ localStart += (Data - DataOrig);
++ break;
+ }
+ }
+- }
+
+- localStart += (Data - DataOrig);
+ return FoundEndOfPicture;
+ }
+
+ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
+ {
+- if (initiallySyncing)
+- return -1; // fill the packet buffer completely until we have synced once
+-
+ int PesPayloadOffset = 0;
+
+ if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= phInvalid)
+ return -1; // not enough data for test
+
+- // just detect end of picture
+- if (state == scanPicture) {
+- // setup local scanner
+- if (localStart < 0) {
+- localScanner = scanner;
+- localStart = 0;
+- }
+- // start where we've stopped at the last run
+- const uchar *data = Data + PesPayloadOffset + localStart;
+- const uchar *limit = Data + Count;
+- // scan data
+- if (ScanForEndOfPicture(data, limit))
++ // setup local scanner
++ if (localStart < 0) {
++ localScanner = scanner;
++ localStart = 0;
++ }
++ // start where we've stopped at the last run
++ const uchar *data = Data + PesPayloadOffset + localStart;
++ const uchar *limit = Data + Count;
++ // scan data
++ startCodeLocationsPrepared = true;
++ if (ScanForEndOfPicture(data, limit)) {
++ // just detect end of picture
++ if (state == scanPicture && !initiallySyncing)
+ return data - Data;
+ }
++ if (initiallySyncing)
++ return -1; // fill the packet buffer completely until we have synced once
+ // just fill up packet and append next start code
+ return PesPayloadOffset + packetTodo + 4;
+ }
+@@ -690,12 +1203,13 @@ private:
+ int frameTodo;
+ int frameSize;
+ int cid;
+- static bool IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize = NULL);
++ static bool IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize = NULL, int *FrameDuration = NULL);
+ public:
+ cAudioRepacker(int Cid);
+ virtual void Reset(void);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int BreakAt(const uchar *Data, int Count);
++ static int GetFrameDuration(const uchar *Data, int Count, int *TrackIndex = NULL);
+ };
+
+ int cAudioRepacker::bitRates[2][3][16] = { // all values are specified as kbits/s
+@@ -711,6 +1225,25 @@ int cAudioRepacker::bitRates[2][3][16] =
+ }
+ };
+
++int cAudioRepacker::GetFrameDuration(const uchar *Data, int Count, int *TrackIndex)
++{
++ int PesPayloadOffset = 0;
++ ePesHeader PH = AnalyzePesHeader(Data, Count, PesPayloadOffset);
++ if (PH < phMPEG1)
++ return -1;
++
++ const uchar *Payload = Data + PesPayloadOffset;
++ const int PayloadCount = Count - PesPayloadOffset;
++
++ int FrameDuration = -1;
++ if ((Data[3] & 0xE0) == 0xC0 && PayloadCount >= 4) {
++ if (IsValidAudioHeader(((Payload[0] << 8 | Payload[1]) << 8 | Payload[2]) << 8 | Payload[3], PH == phMPEG2, NULL, &FrameDuration) && TrackIndex)
++ *TrackIndex = Data[3] - 0xC0;
++ }
++
++ return FrameDuration;
++}
++
+ cAudioRepacker::cAudioRepacker(int Cid)
+ {
+ cid = Cid;
+@@ -726,7 +1259,7 @@ void cAudioRepacker::Reset(void)
+ frameSize = 0;
+ }
+
+-bool cAudioRepacker::IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize)
++bool cAudioRepacker::IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize, int *FrameDuration)
+ {
+ int syncword = (Header & 0xFFF00000) >> 20;
+ int id = (Header & 0x00080000) >> 19;
+@@ -760,32 +1293,36 @@ bool cAudioRepacker::IsValidAudioHeader(
+ if (emphasis == 2) // reserved
+ return false;
+
+- if (FrameSize) {
+- if (bitrate_index == 0)
+- *FrameSize = 0;
+- else {
+- static int samplingFrequencies[2][4] = { // all values are specified in Hz
+- { 44100, 48000, 32000, -1 }, // MPEG 1
+- { 22050, 24000, 16000, -1 } // MPEG 2
+- };
+-
+- static int slots_per_frame[2][3] = {
+- { 12, 144, 144 }, // MPEG 1, Layer I, II, III
+- { 12, 144, 72 } // MPEG 2, Layer I, II, III
+- };
+-
+- int mpegIndex = 1 - id;
+- int layerIndex = 3 - layer;
+-
+- // Layer I (i. e., layerIndex == 0) has a larger slot size
+- int slotSize = (layerIndex == 0) ? 4 : 1; // bytes
+-
+- int br = 1000 * bitRates[mpegIndex][layerIndex][bitrate_index]; // bits/s
+- int sf = samplingFrequencies[mpegIndex][sampling_frequency];
+-
+- int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots
++ if (FrameSize || FrameDuration) {
++ static int samplingFrequencies[2][4] = { // all values are specified in Hz
++ { 44100, 48000, 32000, -1 }, // MPEG 1
++ { 22050, 24000, 16000, -1 } // MPEG 2
++ };
++
++ static int slots_per_frame[2][3] = {
++ { 12, 144, 144 }, // MPEG 1, Layer I, II, III
++ { 12, 144, 72 } // MPEG 2, Layer I, II, III
++ };
++
++ int mpegIndex = 1 - id;
++ int layerIndex = 3 - layer;
++
++ // Layer I (i. e., layerIndex == 0) has a larger slot size
++ int slotSize = (layerIndex == 0) ? 4 : 1; // bytes
++ int sf = samplingFrequencies[mpegIndex][sampling_frequency];
++
++ if (FrameDuration)
++ *FrameDuration = 90000 * 8 * slotSize * slots_per_frame[mpegIndex][layerIndex] / sf;
++
++ if (FrameSize) {
++ if (bitrate_index == 0)
++ *FrameSize = 0;
++ else {
++ int br = 1000 * bitRates[mpegIndex][layerIndex][bitrate_index]; // bits/s
++ int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots
+
+- *FrameSize = (N + padding_bit) * slotSize; // bytes
++ *FrameSize = (N + padding_bit) * slotSize; // bytes
++ }
+ }
+ }
+
+@@ -895,6 +1432,22 @@ void cAudioRepacker::Repack(cRingBufferL
+ todo--;
+ // do we have to start a new packet as the current is done?
+ if (frameTodo > 0) {
++ // try to skip most loops for continuous memory
++ int bite = frameTodo; // jump to next audio frame
++ if (bite > packetTodo)
++ bite = packetTodo; // jump only to next output packet
++ if (--bite > todo)
++ bite = todo; // jump only to next input packet
++ // is there enough payload available to load the scanner?
++ if (bite > 0 && done + bite - pesPayloadOffset >= 4) {
++ data += bite;
++ done += bite;
++ todo -= bite;
++ frameTodo -= bite;
++ packetTodo -= bite;
++ uint32_t *Scanner = (uint32_t *)(data - 4);
++ scanner = ntohl(*Scanner);
++ }
+ if (--frameTodo == 0) {
+ // the current audio frame is is done now. So push out the packet to
+ // start a new packet for the next audio frame.
+@@ -1086,6 +1639,7 @@ public:
+ virtual void Reset(void);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int BreakAt(const uchar *Data, int Count);
++ static int GetFrameDuration(const uchar *Data, int Count, int *TrackIndex = NULL);
+ };
+
+ // frameSizes are in words, i. e. multiply them by 2 to get bytes
+@@ -1112,6 +1666,30 @@ int cDolbyRepacker::frameSizes[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+
++int cDolbyRepacker::GetFrameDuration(const uchar *Data, int Count, int *TrackIndex)
++{
++ int PesPayloadOffset = 0;
++ ePesHeader PH = AnalyzePesHeader(Data, Count, PesPayloadOffset);
++ if (PH < phMPEG1)
++ return -1;
++
++ const uchar *Payload = Data + PesPayloadOffset;
++ const int PayloadCount = Count - PesPayloadOffset;
++
++ if (Data[3] == 0xBD && PayloadCount >= 9 && ((Payload[0] & 0xF0) == 0x80) && Payload[4] == 0x0B && Payload[5] == 0x77 && frameSizes[Payload[8]] > 0) {
++ if (TrackIndex)
++ *TrackIndex = Payload[0] - 0x80;
++
++ static int samplingFrequencies[4] = { // all values are specified in Hz
++ 48000, 44100, 32000, -1
++ };
++
++ return 90000 * 1536 / samplingFrequencies[Payload[8] >> 6];
++ }
++
++ return -1;
++}
++
+ cDolbyRepacker::cDolbyRepacker(void)
+ {
+ pesHeader[0] = 0x00;
+@@ -1521,7 +2099,7 @@ void cTS2PES::store(uint8_t *Data, int C
+ if (repacker)
+ repacker->Repack(resultBuffer, Data, Count);
+ else
+- cRepacker::Put(resultBuffer, Data, Count, Count);
++ cRepacker::PutAllOrNothing(resultBuffer, Data, Count, Count);
+ }
+
+ void cTS2PES::reset_ipack(void)
+@@ -1848,10 +2426,7 @@ void cTS2PES::ts_to_pes(const uint8_t *B
+ // Enable this if you are having problems with signal quality.
+ // 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;
+ }
+@@ -1883,6 +2458,58 @@ void cTS2PES::ts_to_pes(const uint8_t *B
+ #endif /* DVBSETUP */
+ }
+
++// --- cAudioIndexer ---------------------------------------------------------
++
++class cAudioIndexer {
++private:
++ int frameTrack;
++ int frameDuration;
++ int64_t trackTime[MAXAPIDS + MAXDPIDS];
++ int64_t nextIndexTime;
++
++public:
++ cAudioIndexer(void);
++ void Clear(void);
++ void PrepareFrame(const uchar *Data, int Count, int Offset, uchar &PictureType);
++ void ProcessFrame(void);
++ };
++
++cAudioIndexer::cAudioIndexer(void)
++{
++ Clear();
++}
++
++void cAudioIndexer::Clear(void)
++{
++ memset(trackTime, 0, sizeof (trackTime));
++ nextIndexTime = 0;
++ frameTrack = -1;
++}
++
++void cAudioIndexer::PrepareFrame(const uchar *Data, int Count, int Offset, uchar &PictureType)
++{
++ frameDuration = cRemux::GetAudioFrameDuration(Data + Offset, Count - Offset, &frameTrack);
++ if (frameDuration <= 0)
++ return;
++
++ if (Data[Offset + 3] == 0xBD)
++ frameTrack += MAXAPIDS;
++
++ PictureType = (trackTime[frameTrack] >= nextIndexTime) ? I_FRAME : NO_PICTURE;
++}
++
++void cAudioIndexer::ProcessFrame(void)
++{
++ if (frameTrack < 0)
++ return;
++
++ if (trackTime[frameTrack] >= nextIndexTime)
++ nextIndexTime += 90000 / FRAMESPERSEC;
++
++ trackTime[frameTrack] += frameDuration;
++ frameTrack = -1;
++}
++
+ // --- cRingBufferLinearPes --------------------------------------------------
+
+ class cRingBufferLinearPes : public cRingBufferLinear {
+@@ -1910,19 +2537,14 @@ 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 */
+ {
++ h264 = false;
+ 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;
+@@ -1931,7 +2553,7 @@ cRemux::cRemux(int VPid, const int *APid
+ if (VPid)
+ #define TEST_cVideoRepacker
+ #ifdef TEST_cVideoRepacker
+- ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0, 0x00, new cVideoRepacker);
++ ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0, 0x00, new cVideoRepacker(h264));
+ #else
+ ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0);
+ #endif
+@@ -1957,6 +2579,7 @@ cRemux::cRemux(int VPid, const int *APid
+ while (*SPids && numTracks < MAXTRACKS && n < MAXSPIDS)
+ ts2pes[numTracks++] = new cTS2PES(*SPids++, resultBuffer, IPACKS, 0x00, 0x20 + n++);
+ }
++ audioIndexer = (noVideo ? new cAudioIndexer : NULL);
+ }
+
+ cRemux::~cRemux()
+@@ -1964,6 +2587,18 @@ cRemux::~cRemux()
+ for (int t = 0; t < numTracks; t++)
+ delete ts2pes[t];
+ delete resultBuffer;
++ delete audioIndexer;
++}
++
++int cRemux::GetAudioFrameDuration(const uchar *Data, int Count, int *TrackIndex)
++{
++ if (Count <= 4)
++ return -1;
++
++ if (Data[3] == 0xBD)
++ return cDolbyRepacker::GetFrameDuration(Data, Count, TrackIndex);
++
++ return cAudioRepacker::GetFrameDuration(Data, Count, TrackIndex);
+ }
+
+ int cRemux::GetPid(const uchar *Data)
+@@ -1981,12 +2616,33 @@ int cRemux::GetPacketLength(const uchar
+ return -1;
+ }
+
++bool cRemux::IsFrameH264(const uchar *Data, int Length)
++{
++ int PesPayloadOffset;
++ const uchar *limit = Data + Length;
++ if (AnalyzePesHeader(Data, Length, PesPayloadOffset) <= phInvalid)
++ return false; // neither MPEG1 nor MPEG2
++
++ Data += PesPayloadOffset + 3; // move to video payload and skip 00 00 01
++ if (Data < limit) {
++ // cVideoRepacker ensures that in case of H264 we will see an access unit delimiter here
++ if (0x01 == Data[-1] && 9 == Data[0] && 0x00 == Data[-2] && 0x00 == Data[-3])
++ return true;
++ }
++
++ return false;
++}
++
+ int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType)
+ {
+ // Scans the video packet starting at Offset and returns its length.
+ // If the return value is -1 the packet was not completely in the buffer.
+ int Length = GetPacketLength(Data, Count, Offset);
+ if (Length > 0) {
++#ifdef TEST_cVideoRepacker
++ bool FoundPicture = false;
++ bool FoundPictureCodingExtension = false;
++#endif
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) {
+ const uchar *p = Data + Offset + PesPayloadOffset + 2;
+@@ -1999,30 +2655,99 @@ int cRemux::ScanVideoPacket(const uchar
+ if (p[-2] || p[-1] || p[0] != 0x01)
+ pLimit = 0; // skip scanning: packet doesn't start with 0x000001
+ else {
+- switch (p[1]) {
+- case SC_SEQUENCE:
+- case SC_GROUP:
+- case SC_PICTURE:
+- break;
+- default: // skip scanning: packet doesn't start a new sequence, group or picture
+- pLimit = 0;
+- }
++ if (h264) {
++ int nal_unit_type = p[1] & 0x1F;
++ switch (nal_unit_type) {
++ case 9: // access unit delimiter
++ // when the MSB in p[1] is set (which violates H.264) then this is a hint
++ // from cVideoRepacker that this second field of the current frame shall
++ // not be reported as picture.
++ if (p[1] & 0x80)
++ ((uchar *)p)[1] &= ~0x80; // revert the hint and fall through
++ else
++ break;
++ default: // skip scanning: packet doesn't start a new picture
++ pLimit = 0;
++ }
++ }
++ else {
++ switch (p[1]) {
++ case SC_SEQUENCE:
++ case SC_GROUP:
++ case SC_PICTURE:
++ break;
++ default: // skip scanning: packet doesn't start a new sequence, group or picture
++ pLimit = 0;
++ }
++ }
+ }
+ }
+ #endif
+ while (p < pLimit && (p = (const uchar *)memchr(p, 0x01, pLimit - p))) {
+ if (!p[-2] && !p[-1]) { // found 0x000001
+- switch (p[1]) {
+- case SC_PICTURE: PictureType = (p[3] >> 3) & 0x07;
+- return Length;
+- }
++ if (h264) {
++ int nal_unit_type = p[1] & 0x1F;
++ switch (nal_unit_type) {
++ case 9: { // access unit delimiter
++ int primary_pic_type = p[2] >> 5;
++ switch (primary_pic_type) {
++ case 0: // I
++ case 3: // SI
++ case 5: // I, SI
++ PictureType = I_FRAME;
++ break;
++ case 1: // I, P
++ case 4: // SI, SP
++ case 6: // I, SI, P, SP
++ PictureType = P_FRAME;
++ break;
++ case 2: // I, P, B
++ case 7: // I, SI, P, SP, B
++ PictureType = B_FRAME;
++ break;
++ }
++ return Length;
++ }
++ }
++ }
++ else {
++ switch (p[1]) {
++ case SC_PICTURE: PictureType = (p[3] >> 3) & 0x07;
++#ifndef TEST_cVideoRepacker
++ return Length;
++#else
++ FoundPicture = true;
++ break;
++ case 0x01 ... 0xAF: // slice startcodes
++ break;
++ case 0xB5: // extension startcode
++ case 0xB9: // hint from cVideoRepacker
++ if (FoundPicture && p + 2 < pLimit && (p[2] & 0xF0) == 0x80) { // picture coding extension
++ FoundPictureCodingExtension = true;
++ // using 0xB9 instead of 0xB5 for an expected picture coding extension
++ // is a hint from cVideoRepacker that this second field of the current
++ // frame shall not be reported as picture.
++ if (p[1] == 0xB9) {
++ ((uchar *)p)[1] = 0xB5; // revert the hint
++ pLimit = 0; // return with NO_PICTURE below
++ }
++ else
++ return Length;
++ }
++ break;
++#endif
++ }
++ }
+ p += 4; // continue scanning after 0x01ssxxyy
+ }
+ else
+ p += 3; // continue scanning after 0x01xxyy
+ }
+ }
+- PictureType = NO_PICTURE;
++#ifdef TEST_cVideoRepacker
++ if (!FoundPicture || FoundPictureCodingExtension)
++#endif
++ PictureType = NO_PICTURE;
+ return Length;
+ }
+ return -1;
+@@ -2126,26 +2851,14 @@ 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 */
++ 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");
+ }
+ }
+ else if (Count)
+@@ -2158,26 +2871,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) {
++ uchar pt = NO_PICTURE;
++ if (audioIndexer && !Count)
++ audioIndexer->PrepareFrame(data, resultCount, i, pt);
+ if (!synced) {
+ if (PictureType && noVideo)
+-#else
+- if (noVideo) {
+- if (!synced) {
+- if (PictureType)
+-#endif /* SYNCEARLY */
+- *PictureType = I_FRAME;
++ *PictureType = pt;
+ resultSkipped = i; // will drop everything before this position
+ synced = true;
+-#ifdef USE_SYNCEARLY
+- if (!noVideo && syncEarly) fprintf(stderr, "audio: synced early\n");
+-#endif /* SYNCEARLY */
++ if (!noVideo) fprintf(stderr, "audio: synced early\n");
+ }
+ else if (Count)
+ return resultData;
+ else if (PictureType)
+- *PictureType = I_FRAME;
++ *PictureType = pt;
+ }
+ }
+ if (synced) {
+@@ -2198,6 +2906,8 @@ uchar *cRemux::Get(int &Count, uchar *Pi
+ void cRemux::Del(int Count)
+ {
+ resultBuffer->Del(Count);
++ if (audioIndexer && Count > 0)
++ audioIndexer->ProcessFrame();
+ }
+
+ void cRemux::Clear(void)
+@@ -2205,6 +2915,8 @@ void cRemux::Clear(void)
+ for (int t = 0; t < numTracks; t++)
+ ts2pes[t]->Clear();
+ resultBuffer->Clear();
++ if (audioIndexer)
++ audioIndexer->Clear();
+ synced = false;
+ skipped = 0;
+ resultSkipped = 0;
+diff -ruNp vdr-1.7.0-extensions/remux.h vdr-1.7.0-ext-h264-s2ng-speedup/remux.h
+--- vdr-1.7.0-extensions/remux.h 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/remux.h 2009-04-12 23:57:23.000000000 +0200
+@@ -33,35 +33,32 @@ ePesHeader AnalyzePesHeader(const uchar
+ #define MAXTRACKS 64
+
+ class cTS2PES;
++class cAudioIndexer;
+
+ class cRemux {
+ private:
+ bool exitOnFailure;
+ bool noVideo;
++ bool h264;
+ int numUPTerrors;
+ bool synced;
+-#ifdef USE_SYNCEARLY
+ bool syncEarly;
+-#endif /* SYNCEARLY */
+ int skipped;
+ cTS2PES *ts2pes[MAXTRACKS];
+ int numTracks;
+ cRingBufferLinear *resultBuffer;
+ int resultSkipped;
++ cAudioIndexer *audioIndexer;
+ int GetPid(const uchar *Data);
++ int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
+ 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.
++ ///< exit" in case of problems with the data stream. 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
+@@ -87,7 +84,8 @@ public:
+ ///< settings as they are.
+ static void SetBrokenLink(uchar *Data, int Length);
+ static int GetPacketLength(const uchar *Data, int Count, int Offset);
+- static int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
++ static int GetAudioFrameDuration(const uchar *Data, int Count, int *TrackIndex = NULL);
++ static bool IsFrameH264(const uchar *Data, int Length);
+ };
+
+ #endif // __REMUX_H
+diff -ruNp vdr-1.7.0-extensions/svdrp.c vdr-1.7.0-ext-h264-s2ng-speedup/svdrp.c
+--- vdr-1.7.0-extensions/svdrp.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/svdrp.c 2009-04-12 23:57:23.000000000 +0200
+@@ -1362,8 +1362,10 @@ void cSVDRP::CmdPLAY(const char *Option)
+ int x = sscanf(option, "%d:%d:%d.%d", &h, &m, &s, &f);
+ if (x == 1)
+ pos = h;
+- else if (x >= 3)
+- pos = (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
++ else if (x >= 3) {
++ int FramesPerSec = cUnbufferedFile::GetFramesPerSec(recording->FileName());
++ pos = (h * 3600 + m * 60 + s) * FramesPerSec + f - 1;
++ }
+ }
+ cResumeFile resume(recording->FileName());
+ if (pos <= 0)
+diff -ruNp vdr-1.7.0-extensions/thread.c vdr-1.7.0-ext-h264-s2ng-speedup/thread.c
+--- vdr-1.7.0-extensions/thread.c 2008-04-13 13:53:56.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/thread.c 2009-04-12 23:57:23.000000000 +0200
+@@ -219,9 +219,20 @@ cThread::~cThread()
+ free(description);
+ }
+
++int cThread::GetPriority(void)
++{
++ errno = 0;
++ int Priority = getpriority(PRIO_PROCESS, 0);
++ if (Priority == -1 && errno != 0) {
++ LOG_ERROR;
++ Priority = 0;
++ }
++ return Priority;
++}
++
+ void cThread::SetPriority(int Priority)
+ {
+- if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
++ if (setpriority(PRIO_PROCESS, 0, max(-20, min(Priority, 19))) < 0)
+ LOG_ERROR;
+ }
+
+diff -ruNp vdr-1.7.0-extensions/thread.h vdr-1.7.0-ext-h264-s2ng-speedup/thread.h
+--- vdr-1.7.0-extensions/thread.h 2007-02-24 17:13:28.000000000 +0100
++++ vdr-1.7.0-ext-h264-s2ng-speedup/thread.h 2009-04-12 23:57:23.000000000 +0200
+@@ -86,6 +86,7 @@ private:
+ static tThreadId mainThreadId;
+ static void *StartThread(cThread *Thread);
+ protected:
++ int GetPriority(void);
+ void SetPriority(int Priority);
+ void Lock(void) { mutex.Lock(); }
+ void Unlock(void) { mutex.Unlock(); }
+diff -ruNp vdr-1.7.0-extensions/tools.c vdr-1.7.0-ext-h264-s2ng-speedup/tools.c
+--- vdr-1.7.0-extensions/tools.c 2008-03-05 18:23:47.000000000 +0100
++++ vdr-1.7.0-ext-h264-s2ng-speedup/tools.c 2009-04-12 23:57:23.000000000 +0200
+@@ -27,6 +27,8 @@ extern "C" {
+ #include <utime.h>
+ #include "i18n.h"
+ #include "thread.h"
++#include "remux.h"
++#include "recording.h"
+
+ int SysLogLevel = 3;
+
+@@ -82,6 +84,14 @@ ssize_t safe_write(int filedes, const vo
+ return p < 0 ? p : written;
+ }
+
++int readchar(int filedes)
++{
++ char c;
++ if (safe_read(filedes, &c, sizeof(c)) != 1)
++ return -1;
++ return c;
++}
++
+ void writechar(int filedes, char c)
+ {
+ safe_write(filedes, &c, sizeof(c));
+@@ -140,11 +150,8 @@ char *strreplace(char *s, char c1, char
+ {
+ if (s) {
+ char *p = s;
+- while (*p) {
+- if (*p == c1)
+- *p = c2;
+- p++;
+- }
++ while ((p = strchr(p, c1)))
++ *p++ = c2;
+ }
+ return s;
+ }
+@@ -1630,6 +1637,112 @@ cUnbufferedFile *cUnbufferedFile::Create
+ return File;
+ }
+
++int cUnbufferedFile::GetFramesPerSec(const char *FileName)
++{
++ // use this constant as a fallback value
++ int FramesPerSec = FRAMESPERSEC;
++ // open the file an determine frames per second
++ cFileName fn(FileName, false);
++ cUnbufferedFile *f = fn.Open();
++ if (f) {
++ FramesPerSec = f->GetFramesPerSec();
++ fn.Close();
++ }
++ return FramesPerSec;
++}
++
++#define ADD_H264_SUPPORT 1
++
++#ifdef ADD_H264_SUPPORT
++#include "h264parser.h"
++#endif
++
++int cUnbufferedFile::GetFramesPerSec(void)
++{
++ // use this constant as a fallback value
++ int FramesPerSec = FRAMESPERSEC;
++ // rember current file position to restore later
++ off_t OrigPos = curpos;
++ // seek to the beginning and read a chunk of data
++ if (0 == Seek(0, SEEK_SET)) {
++ uchar Data[2048];
++ ssize_t Count = Read(Data, sizeof(Data));
++ if (Count > 0) {
++ // this chunk of data should actually be a PES packet
++ uchar *Limit = Data + Count;
++ int PesPayloadOffset = 0;
++ if (AnalyzePesHeader(Data, Count, PesPayloadOffset) >= phMPEG1) {
++ // we need a video stream -- radio recordings use the default
++ if ((Data[3] & 0xF0) == 0xE0) {
++ uchar *p = Data + PesPayloadOffset;
++#ifdef ADD_H264_SUPPORT
++ // check whether this is a H.264 video frame
++ if (cRemux::IsFrameH264(Data, Count)) {
++ // need to have a H264 parser since picture timing is rather complex
++ H264::cParser H264parser(false);
++ // send NAL units to parser until it is able to provide frames per second
++ while (p < Limit) {
++ // find next NAL unit
++ uchar *pNext = (uchar *)memmem(p + 4, Limit - (p + 4), "\x00\x00\x01", 3);
++ if (!pNext) // just pass the remainder
++ pNext = Limit;
++ H264parser.PutNalUnitData(p, pNext - p);
++ // process NAL unit and check for frames per second
++ H264parser.Process();
++ int FPS = H264parser.Context().GetFramesPerSec();
++ if (FPS != -1) { // there we are ;-)
++ FramesPerSec = FPS;
++fprintf(stderr, "FramesPerSec: %d\n", FramesPerSec);
++ break;
++ }
++ // continue with next NAL unit
++ p = pNext;
++ }
++ }
++ else {
++#endif
++ // thanks to cVideoRepacker, the payload starts with a sequence header
++ if (p + 12 <= Limit) {
++ if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xB3) {
++ uint32_t frame_rate_code = p[7] & 0x0F;
++ uint32_t frame_rate_extension_n = 0;
++ uint32_t frame_rate_extension_d = 0;
++ // now we need to have a look at the next startcode,
++ // as it might be a sequence extension
++ p = (uchar *)memmem(p + 12, Limit - (p + 12), "\x00\x00\x01", 3);
++ if (p && p + 4 < Limit && p[3] == 0xB5) { // extension start code
++ if (p + 5 < Limit && (p[4] >> 4) == 0x1) { // sequence extension
++ if (p + 10 < Limit) {
++ frame_rate_extension_n = (p[9] & 0x60) >> 5;
++ frame_rate_extension_d = (p[9] & 0x1F);
++ }
++ }
++ }
++ // calculate frame rate and round it for compatibility
++ if (0x1 <= frame_rate_code && frame_rate_code <= 0x8) {
++ static const int n[] = { -1, 24000, 24, 25, 30000, 30, 50, 60000, 60 };
++ static const int d[] = { -1, 1001, 1, 1, 1001, 1, 1, 1001, 1 };
++ double frame_rate = n[frame_rate_code] * (frame_rate_extension_n + 1)
++ / (double)(d[frame_rate_code] * (frame_rate_extension_d + 1));
++ FramesPerSec = (int)frame_rate;
++ if (frame_rate - FramesPerSec > 0.5)
++ FramesPerSec++;
++fprintf(stderr, "FramesPerSec: %d\n", FramesPerSec);
++ }
++ }
++ }
++#ifdef ADD_H264_SUPPORT
++ }
++#endif
++ }
++ }
++ }
++ }
++ // restore original position
++ Seek(OrigPos, SEEK_SET);
++ return FramesPerSec;
++}
++
+ // --- cLockFile -------------------------------------------------------------
+
+ #define LOCKFILENAME ".lock-vdr"
+diff -ruNp vdr-1.7.0-extensions/tools.h vdr-1.7.0-ext-h264-s2ng-speedup/tools.h
+--- vdr-1.7.0-extensions/tools.h 2008-02-17 14:41:27.000000000 +0100
++++ vdr-1.7.0-ext-h264-s2ng-speedup/tools.h 2009-04-12 23:57:23.000000000 +0200
+@@ -167,6 +167,7 @@ public:
+
+ ssize_t safe_read(int filedes, void *buffer, size_t size);
+ ssize_t safe_write(int filedes, const void *buffer, size_t size);
++int readchar(int filedes);
+ void writechar(int filedes, char c);
+ int WriteAllOrNothing(int fd, const uchar *Data, int Length, int TimeoutMs = 0, int RetryMs = 0);
+ ///< Writes either all Data to the given file descriptor, or nothing at all.
+@@ -353,6 +354,8 @@ public:
+ ssize_t Read(void *Data, size_t Size);
+ ssize_t Write(const void *Data, size_t Size);
+ static cUnbufferedFile *Create(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
++ static int GetFramesPerSec(const char *FileName);
++ int GetFramesPerSec(void);
+ };
+
+ class cLockFile {
+diff -ruNp vdr-1.7.0-extensions/transfer.c vdr-1.7.0-ext-h264-s2ng-speedup/transfer.c
+--- vdr-1.7.0-extensions/transfer.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/transfer.c 2009-04-12 23:57:23.000000000 +0200
+@@ -19,11 +19,7 @@ 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 */
++ remux = new cRemux(VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids, false, true);
+ }
+
+ cTransfer::~cTransfer()
+diff -ruNp vdr-1.7.0-extensions/vdr.c vdr-1.7.0-ext-h264-s2ng-speedup/vdr.c
+--- vdr-1.7.0-extensions/vdr.c 2009-04-09 20:48:48.000000000 +0200
++++ vdr-1.7.0-ext-h264-s2ng-speedup/vdr.c 2009-04-12 23:57:23.000000000 +0200
+@@ -580,10 +580,6 @@ int main(int argc, char *argv[])
+ printf(" DOLBYINREC\n");
+ #endif /* DOLBYINREC */
+
+-#ifdef USE_DVBPLAYER
+- printf(" DVBPLAYER\n");
+-#endif /* DVBPLAYER */
+-
+ #ifdef USE_DVBSETUP
+ printf(" DVBSETUP\n");
+ #endif /* DVBSETUP */
+@@ -704,10 +700,6 @@ int main(int argc, char *argv[])
+ printf(" STREAMDEVEXT\n");
+ #endif /* STREAMDEVEXT */
+
+-#ifdef USE_SYNCEARLY
+- printf(" SYNCEARLY\n");
+-#endif /* SYNCEARLY */
+-
+ #ifdef USE_TIMERCMD
+ printf(" TIMERCMD\n");
+ #endif /* TIMERCMD */
diff --git a/vdr/extensions/vdr-1.7.0_extensions.diff b/vdr/extensions/vdr-1.7.0_extensions.diff
new file mode 100644
index 0000000..680aa02
--- /dev/null
+++ b/vdr/extensions/vdr-1.7.0_extensions.diff
@@ -0,0 +1,28873 @@
+diff -ruNp vdr-1.7.0/channels.c vdr-1.7.0-extensions/channels.c
+--- vdr-1.7.0/channels.c 2008-04-12 15:49:12.000000000 +0200
++++ vdr-1.7.0-extensions/channels.c 2009-04-09 20:48:48.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
+@@ -216,6 +219,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 = DVBFE_INVERSION_AUTO;
+ bandwidth = DVBFE_BANDWIDTH_AUTO;
+@@ -241,6 +247,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;
+@@ -269,6 +278,9 @@ cChannel::~cChannel()
+ free(shortName);
+ free(provider);
+ free(portalName);
++#ifdef USE_PLUGINPARAM
++ free(pluginParam);
++#endif /* PLUGINPARAM */
+ }
+
+ cChannel& cChannel::operator= (const cChannel &Channel)
+@@ -277,6 +289,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;
+ }
+@@ -338,6 +353,9 @@ void cChannel::CopyTransponderData(const
+ alpha = Channel->alpha;
+ priority = Channel->priority;
+ rollOff = Channel->rollOff;
++#ifdef USE_PLUGINPARAM
++ if (IsPlug()) pluginParam = strcpyrealloc(pluginParam, Channel->pluginParam);
++#endif /* PLUGINPARAM */
+ }
+ }
+
+@@ -375,6 +393,24 @@ bool cChannel::SetSatTransponderData(int
+ return true;
+ }
+
++#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::SetCableTransponderData(int Source, int Frequency, int Modulation, int Srate, int CoderateH)
+ {
+ if (source != Source || frequency != Frequency || modulation != Modulation || srate != Srate || coderateH != CoderateH) {
+@@ -447,6 +483,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();
+ }
+@@ -472,6 +511,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
+
+@@ -665,7 +718,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);
+@@ -681,6 +738,9 @@ cString cChannel::ParametersToString(voi
+ ST(" S ") q += PrintParameter(q, 'S', MapToUser(system, SystemValues));
+ ST(" T") q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues));
+ ST(" T") q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues));
++#ifdef USE_PLUGINPARAM
++ ST("P ") snprintf(buffer, sizeof(buffer), "%s", pluginParam);
++#endif /* PLUGINPARAM */
+ return buffer;
+ }
+
+@@ -702,7 +762,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 'A': s = ParseParameter(s, alpha, AlphaValues); break;
+ case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break;
+@@ -817,7 +881,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)
+@@ -908,6 +976,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);
+@@ -1153,6 +1224,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.7.0/channels.h vdr-1.7.0-extensions/channels.h
+--- vdr-1.7.0/channels.h 2008-04-12 15:46:50.000000000 +0200
++++ vdr-1.7.0-extensions/channels.h 2009-04-09 20:48:48.000000000 +0200
+@@ -118,6 +118,9 @@ private:
+ char *shortName;
+ char *provider;
+ char *portalName;
++#ifdef USE_PLUGINPARAM
++ char *pluginParam;
++#endif /* PLUGINPARAM */
+ int __BeginData__;
+ int frequency; // MHz
+ int source;
+@@ -174,6 +177,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; }
+@@ -212,6 +218,9 @@ public:
+ int RollOff(void) const { return rollOff; }
+ 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); }
+@@ -219,12 +228,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, int Modulation, int System, int RollOff);
+ 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, int Alpha, int Priority);
+ 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.7.0/config.c vdr-1.7.0-extensions/config.c
+--- vdr-1.7.0/config.c 2008-02-17 14:39:00.000000000 +0100
++++ vdr-1.7.0-extensions/config.c 2009-04-09 20:48:48.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.7.0/config.h vdr-1.7.0-extensions/config.h
+--- vdr-1.7.0/config.h 2008-04-12 15:02:10.000000000 +0200
++++ vdr-1.7.0-extensions/config.h 2009-04-09 20:48:48.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.7.0/cutter.c vdr-1.7.0-extensions/cutter.c
+--- vdr-1.7.0/cutter.c 2008-01-13 13:22:21.000000000 +0100
++++ vdr-1.7.0-extensions/cutter.c 2009-04-09 20:48:48.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,72 @@ 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
++ 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 +443,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 +457,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 +481,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 +522,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.7.0/cutter.h vdr-1.7.0-extensions/cutter.h
+--- vdr-1.7.0/cutter.h 2002-06-22 12:03:15.000000000 +0200
++++ vdr-1.7.0-extensions/cutter.h 2009-04-09 20:48:48.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.7.0/device.c vdr-1.7.0-extensions/device.c
+--- vdr-1.7.0/device.c 2008-04-12 16:12:14.000000000 +0200
++++ vdr-1.7.0-extensions/device.c 2009-04-09 20:48:48.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 (device[i]->IsPrimaryDevice()) {
++ 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 (device[i]->IsPrimaryDevice())
++ 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;
+@@ -399,6 +525,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
+@@ -423,6 +553,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 <<= 2; imp |= GetClippedNumProvidedSystems(2, device[i]) - 1; // avoid cards which support multiple delivery systems
+ 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)
+@@ -432,7 +565,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];
+@@ -473,6 +630,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;
+@@ -717,7 +886,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;
+@@ -745,6 +918,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
+@@ -774,7 +965,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;
+ }
+@@ -795,7 +993,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);
+@@ -805,15 +1014,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;
+@@ -829,6 +1074,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)
+@@ -861,7 +1110,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();
+ }
+@@ -1121,7 +1374,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:
+@@ -1185,7 +1443,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)
+@@ -1310,7 +1572,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.7.0/device.h vdr-1.7.0-extensions/device.h
+--- vdr-1.7.0/device.h 2008-04-12 13:11:23.000000000 +0200
++++ vdr-1.7.0-extensions/device.h 2009-04-09 20:48:48.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);
+@@ -243,17 +284,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; }
+@@ -271,6 +325,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.7.0/dvbdevice.c vdr-1.7.0-extensions/dvbdevice.c
+--- vdr-1.7.0/dvbdevice.c 2008-04-13 16:15:35.000000000 +0200
++++ vdr-1.7.0-extensions/dvbdevice.c 2009-04-09 20:48:48.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, dvbfe_delsys FrontendType)
+ {
+ fd_frontend = Fd_Frontend;
++#ifdef USE_ROTOR
++ SendDiseqc = false;
++#endif /* ROTOR */
+ cardIndex = CardIndex;
+ frontendType = FrontendType;
+ tuneTimeout = 0;
+@@ -164,6 +176,19 @@ bool cDvbTuner::Locked(int TimeoutMs)
+ return tunerStatus >= tsLocked;
+ }
+
++#ifdef USE_ROTOR
++bool cDvbTuner::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
++{
++ cMutexLock MutexLock(&mutex);
++ if ((!DVBFE_DELSYS_DVBS & !DVBFE_DELSYS_DVBS2) || 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) {
+@@ -225,6 +250,9 @@ bool cDvbTuner::SetFrontend(void)
+ }
+ }
+ diseqcCommands = diseqc->Commands();
++#ifdef USE_DVBSETUP
++ isyslog("Sent DISEQC command: %s", diseqcCommands);
++#endif /* DVBSETUP */
+ }
+ frequency -= diseqc->Lof();
+ }
+@@ -286,6 +314,22 @@ bool cDvbTuner::SetFrontend(void)
+ feinfo.delivery = Frontend.delivery;
+ CHECK(ioctl(fd_frontend, DVBFE_GET_INFO, &feinfo)); //switch system
+ }
++#ifdef USE_ATSC
++ else if (frontendType & DVBFE_DELSYS_ATSC) {
++ // Frequency and symbol rate:
++
++ Frontend.frequency = FrequencyToHz(channel.Frequency());
++ Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
++ Frontend.delsys.atsc.modulation = dvbfe_modulation(channel.Modulation());
++
++ tuneTimeout = DVBC_TUNE_TIMEOUT;
++ lockTimeout = DVBC_LOCK_TIMEOUT;
++
++ dvbfe_info feinfo;
++ feinfo.delivery = Frontend.delivery;
++ CHECK(ioctl(fd_frontend, DVBFE_GET_INFO, &feinfo)); //switch system
++ }
++#endif /* ATSC */
+ else if (frontendType & DVBFE_DELSYS_DVBT) {
+ Frontend.delivery = DVBFE_DELSYS_DVBT;
+ Frontend.frequency = FrequencyToHz(channel.Frequency());
+@@ -328,6 +372,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;
+@@ -463,6 +513,10 @@ cDvbDevice::cDvbDevice(int n)
+ if (ioctl(fd_frontend, DVBFE_GET_DELSYS, &frontendType) >= 0) {
+ const char **DeliverySystem = DeliverySystems;
+ cString ds;
++#ifdef USE_DVBSETUP
++ if (Setup.ChannelBlockerMode == 4)
++ frontendType = n == Setup.PrimaryDVB - 1 ? DVBFE_DELSYS_DUMMY : frontendType;
++#endif /* DVBSETUP */
+ for (int i = 0; i < 32; i++) {
+ if (frontendType & (1u << i)) {
+ numProvidedSystems++;
+@@ -694,6 +748,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;
+@@ -799,14 +860,43 @@ 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 & (DVBFE_DELSYS_DVBS | DVBFE_DELSYS_DVBS2))) {
++ 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 & DVBFE_DELSYS_DVBC)
++#ifdef USE_ATSC
++ || type == cSource::stCable && (frontendType & DVBFE_DELSYS_ATSC)
++ || type == cSource::stTerr && (frontendType & DVBFE_DELSYS_ATSC)
++#endif /* ATSC */
+ || type == cSource::stSat && (frontendType & (DVBFE_DELSYS_DVBS | DVBFE_DELSYS_DVBS2))
+ || type == cSource::stTerr && (frontendType & DVBFE_DELSYS_DVBT);
+ }
+
+ 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()) && 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 */
+ if (!ProvidesSource(Channel->Source()))
+ return false; // doesn't provide source
+ if (!cSource::IsSat(Channel->Source()))
+@@ -818,10 +908,31 @@ bool cDvbDevice::ProvidesTransponder(con
+
+ 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()) && 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 (ProvidesTransponder(Channel)) {
+ result = hasPriority;
+ if (Priority >= 0 && Receiving(true)) {
+@@ -882,7 +993,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
+ );
+@@ -938,6 +1053,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.7.0/dvbdevice.h vdr-1.7.0-extensions/dvbdevice.h
+--- vdr-1.7.0/dvbdevice.h 2008-04-12 13:20:48.000000000 +0200
++++ vdr-1.7.0-extensions/dvbdevice.h 2009-04-09 20:48:48.000000000 +0200
+@@ -73,6 +73,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.7.0/dvbosd.c vdr-1.7.0-extensions/dvbosd.c
+--- vdr-1.7.0/dvbosd.c 2007-09-16 10:55:54.000000000 +0200
++++ vdr-1.7.0-extensions/dvbosd.c 2009-04-09 20:48:48.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,52 @@ 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.7.0/dvbplayer.c vdr-1.7.0-extensions/dvbplayer.c
+--- vdr-1.7.0/dvbplayer.c 2008-02-09 16:10:54.000000000 +0100
++++ vdr-1.7.0-extensions/dvbplayer.c 2009-04-09 20:48:48.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,156 @@ 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 +577,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 +599,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 +629,46 @@ 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 +688,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 +708,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 +744,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 +765,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 +954,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.7.0/eit.c vdr-1.7.0-extensions/eit.c
+--- vdr-1.7.0/eit.c 2008-04-13 13:27:06.000000000 +0200
++++ vdr-1.7.0-extensions/eit.c 2009-04-09 20:48:48.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.7.0/eitscan.c vdr-1.7.0-extensions/eitscan.c
+--- vdr-1.7.0/eitscan.c 2006-01-07 15:10:17.000000000 +0100
++++ vdr-1.7.0-extensions/eitscan.c 2009-04-09 20:48:48.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.7.0/epg.c vdr-1.7.0-extensions/epg.c
+--- vdr-1.7.0/epg.c 2008-02-16 17:09:12.000000000 +0100
++++ vdr-1.7.0-extensions/epg.c 2009-04-09 20:48:48.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.7.0/epg.h vdr-1.7.0-extensions/epg.h
+--- vdr-1.7.0/epg.h 2006-10-07 15:47:19.000000000 +0200
++++ vdr-1.7.0-extensions/epg.h 2009-04-09 20:48:48.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.7.0/HISTORY-liemikuutio vdr-1.7.0-extensions/HISTORY-liemikuutio
+--- vdr-1.7.0/HISTORY-liemikuutio 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/HISTORY-liemikuutio 2009-04-09 20:48:48.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.7.0/iconpatch.c vdr-1.7.0-extensions/iconpatch.c
+--- vdr-1.7.0/iconpatch.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/iconpatch.c 2009-04-09 20:48:48.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.7.0/iconpatch.h vdr-1.7.0-extensions/iconpatch.h
+--- vdr-1.7.0/iconpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/iconpatch.h 2009-04-09 20:48:48.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.7.0/keys.h vdr-1.7.0-extensions/keys.h
+--- vdr-1.7.0/keys.h 2007-08-26 14:34:50.000000000 +0200
++++ vdr-1.7.0-extensions/keys.h 2009-04-09 20:48:48.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.7.0/lirc.c vdr-1.7.0-extensions/lirc.c
+--- vdr-1.7.0/lirc.c 2006-05-28 10:48:13.000000000 +0200
++++ vdr-1.7.0-extensions/lirc.c 2009-04-09 20:48:48.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.7.0/livebuffer.c vdr-1.7.0-extensions/livebuffer.c
+--- vdr-1.7.0/livebuffer.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/livebuffer.c 2009-04-09 20:48:48.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.7.0/livebuffer.h vdr-1.7.0-extensions/livebuffer.h
+--- vdr-1.7.0/livebuffer.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/livebuffer.h 2009-04-09 20:48:48.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.7.0/mainmenuitemsprovider.h vdr-1.7.0-extensions/mainmenuitemsprovider.h
+--- vdr-1.7.0/mainmenuitemsprovider.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/mainmenuitemsprovider.h 2009-04-09 20:48:48.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.7.0/Make.config.template vdr-1.7.0-extensions/Make.config.template
+--- vdr-1.7.0/Make.config.template 2008-01-13 13:54:09.000000000 +0100
++++ vdr-1.7.0-extensions/Make.config.template 2009-04-09 20:48:48.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 STREAMDEVEXT
++DEFINES += -DUSE_STREAMDEVEXT
++endif
++
++ifdef SYNCEARLY
++DEFINES += -DUSE_SYNCEARLY
++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.7.0/Makefile vdr-1.7.0-extensions/Makefile
+--- vdr-1.7.0/Makefile 2008-02-29 22:43:03.000000000 +0100
++++ vdr-1.7.0-extensions/Makefile 2009-04-09 20:48:48.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.7.0/MANUAL vdr-1.7.0-extensions/MANUAL
+--- vdr-1.7.0/MANUAL 2008-02-24 11:09:17.000000000 +0100
++++ vdr-1.7.0-extensions/MANUAL 2009-04-09 20:48:48.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.7.0/menu.c vdr-1.7.0-extensions/menu.c
+--- vdr-1.7.0/menu.c 2008-04-12 13:37:17.000000000 +0200
++++ vdr-1.7.0-extensions/menu.c 2009-04-09 20:48:48.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 /* MENUORG */
+
+ #define MAXWAIT4EPGINFO 3 // seconds
+ #define MODETIMEOUT 3 // seconds
+@@ -189,10 +205,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)
+@@ -221,6 +243,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));
+@@ -252,6 +277,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));
++#ifdef USE_PLUGINPARAM
++ ST("P ") Add(new cMenuEditStrItem( tr("Parameters"), pluginParam, sizeof(pluginParam), tr(FileNameChars)));
++#endif /* PLUGINPARAM */
+ ST(" T") Add(new cMenuEditMapItem( tr("Alpha"), &data.alpha, AlphaValues));
+ ST(" T") Add(new cMenuEditMapItem( tr("Priority"), &data.priority, PriorityValues));
+ ST(" S ") Add(new cMenuEditMapItem( tr("Rolloff"), &data.rollOff, RollOffValues));
+@@ -269,9 +297,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 {
+@@ -280,6 +314,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);
+@@ -342,6 +379,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());
+ }
+@@ -372,6 +419,9 @@ public:
+ cMenuChannels(void);
+ ~cMenuChannels();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuChannels"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuChannels::cMenuChannels(void)
+@@ -501,6 +551,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());
+@@ -526,6 +579,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());
+@@ -638,14 +694,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();
+@@ -685,6 +774,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) {
+@@ -712,13 +807,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; }
+@@ -727,6 +846,9 @@ public:
+ cMenuTimerItem::cMenuTimerItem(cTimer *Timer)
+ {
+ timer = Timer;
++#ifdef USE_TIMERINFO
++ diskStatus = ' ';
++#endif /* TIMERINFO */
+ Set();
+ }
+
+@@ -752,8 +874,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 ? " " : "",
+@@ -763,8 +933,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 {
+@@ -777,14 +1003,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)) {
+@@ -795,6 +1038,9 @@ cMenuTimers::cMenuTimers(void)
+ SetCurrent(First());
+ SetHelpKeys();
+ Timers.IncBeingEdited();
++#ifdef USE_TIMERINFO
++ actualiseDiskStatus = true;
++#endif /* TIMERINFO */
+ }
+
+ cMenuTimers::~cMenuTimers()
+@@ -833,7 +1079,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
+@@ -892,6 +1142,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;
+@@ -900,18 +1261,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)
+@@ -941,6 +1324,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());
+ }
+@@ -988,7 +1374,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; }
+@@ -998,12 +1389,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);
+ }
+
+@@ -1018,7 +1416,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)
+ {
+@@ -1027,17 +1447,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;
+ }
+@@ -1063,13 +1520,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;
+@@ -1081,7 +1546,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 */
+ }
+ }
+ }
+@@ -1090,6 +1559,20 @@ 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;
+@@ -1230,6 +1713,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)
+@@ -1255,6 +1742,20 @@ 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();
+@@ -1488,6 +1989,7 @@ eOSState cMenuSchedule::ProcessKey(eKeys
+
+ // --- cMenuCommands ---------------------------------------------------------
+
++#ifndef USE_TIMERCMD
+ class cMenuCommands : public cOsdMenu {
+ private:
+ cCommands *commands;
+@@ -1497,7 +1999,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)
+@@ -1519,6 +2025,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) {
+@@ -1569,6 +2081,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)
+@@ -1748,6 +2263,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)
+@@ -1763,6 +2281,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());
+ }
+@@ -1823,7 +2344,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'
+ }
+@@ -1839,13 +2364,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;
+@@ -1922,7 +2637,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);
+@@ -1972,13 +2692,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 */
+ }
+ }
+ }
+@@ -2076,12 +2826,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();
+@@ -2090,7 +2862,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;
+@@ -2139,6 +2930,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;
+@@ -2154,6 +2948,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)
+@@ -2189,12 +2986,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));
+@@ -2216,6 +3022,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();
+ }
+@@ -2264,6 +3088,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)
+@@ -2286,6 +3116,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;
+ }
+
+@@ -2293,12 +3142,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)
+@@ -2315,11 +3170,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));
+@@ -2328,6 +3191,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();
+@@ -2384,6 +3256,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;
+@@ -2394,6 +3270,9 @@ private:
+ public:
+ cMenuSetupDVB(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupDvb"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupDVB::cMenuSetupDVB(void)
+@@ -2422,6 +3301,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()));
+@@ -2442,6 +3336,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();
+@@ -2523,6 +3425,9 @@ private:
+ public:
+ cMenuSetupLNB(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupLnb"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupLNB::cMenuSetupLNB(void)
+@@ -2537,6 +3442,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));
+@@ -2553,6 +3475,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;
+@@ -2603,6 +3529,9 @@ private:
+ public:
+ cMenuSetupCAM(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupCam"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupCAM::cMenuSetupCAM(void)
+@@ -2676,12 +3605,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));
+@@ -2690,14 +3680,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 ------------------------------------------------------
+@@ -2715,6 +3752,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)
+@@ -2727,13 +3789,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));
+@@ -2741,9 +3838,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 {
+@@ -2766,6 +3931,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)
+@@ -2815,6 +3983,9 @@ private:
+ public:
+ cMenuSetup(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetup"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetup::cMenuSetup(void)
+@@ -2838,6 +4009,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));
+@@ -2868,6 +4042,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) {
+@@ -2904,24 +4081,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)
+@@ -2931,38 +4175,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();
+@@ -2971,13 +4332,40 @@ void cMenuMain::Set(void)
+ 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) {
+@@ -2985,6 +4373,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());
+@@ -2999,6 +4390,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;
+ }
+@@ -3019,6 +4413,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;
+@@ -3026,6 +4423,12 @@ bool cMenuMain::Update(bool Force)
+ result = true;
+ }
+
++#ifdef USE_SETUP
++ // adjust nrDynamicMenuEntries
++ if (fMenu != NULL)
++ nrDynamicMenuEntries = fMenu->Index();
++#endif /* SETUP */
++
+ return result;
+ }
+
+@@ -3036,13 +4439,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) {
+@@ -3061,6 +4520,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())
+@@ -3072,9 +4534,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)
+@@ -3091,9 +4604,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) {
+@@ -3237,7 +4808,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;
+ }
+ }
+@@ -3295,6 +4873,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:
+@@ -3454,6 +5039,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:
+@@ -3703,6 +5299,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;
+@@ -3747,12 +5347,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()
+@@ -3809,11 +5454,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 */
+ }
+ }
+
+@@ -3860,15 +5519,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));
+ }
+ }
+@@ -3999,12 +5678,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;
+@@ -4012,10 +5701,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);
+ }
+@@ -4023,12 +5730,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);
+@@ -4123,6 +5885,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) {
+@@ -4244,8 +6009,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();
+ }
+@@ -4258,8 +6030,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 */
+ }
+ }
+ }
+@@ -4288,11 +6074,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))
+@@ -4314,7 +6132,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));
+@@ -4336,6 +6158,9 @@ eOSState cReplayControl::ProcessKey(eKey
+ {
+ if (!Active())
+ return osEnd;
++#ifdef USE_JUMPPLAY
++ marks.Reload();
++#endif /* JUMPPLAY */
+ if (visible) {
+ if (timeoutShow && time(NULL) > timeoutShow) {
+ Hide();
+@@ -4353,7 +6178,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:
+@@ -4371,25 +6221,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:
+@@ -4409,7 +6316,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.7.0/menu.h vdr-1.7.0-extensions/menu.h
+--- vdr-1.7.0/menu.h 2008-02-10 17:01:53.000000000 +0100
++++ vdr-1.7.0-extensions/menu.h 2009-04-09 20:48:48.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.7.0/menuitems.c vdr-1.7.0-extensions/menuitems.c
+--- vdr-1.7.0/menuitems.c 2008-04-12 14:05:25.000000000 +0200
++++ vdr-1.7.0-extensions/menuitems.c 2009-04-09 20:48:48.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.7.0/menuitems.h vdr-1.7.0-extensions/menuitems.h
+--- vdr-1.7.0/menuitems.h 2008-04-12 14:03:59.000000000 +0200
++++ vdr-1.7.0-extensions/menuitems.h 2009-04-09 20:48:48.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;
+@@ -185,6 +224,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.7.0/menuorgpatch.h vdr-1.7.0-extensions/menuorgpatch.h
+--- vdr-1.7.0/menuorgpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/menuorgpatch.h 2009-04-09 20:48:48.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.7.0/osdbase.c vdr-1.7.0-extensions/osdbase.c
+--- vdr-1.7.0/osdbase.c 2008-02-17 12:33:04.000000000 +0100
++++ vdr-1.7.0-extensions/osdbase.c 2009-04-09 20:48:48.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.7.0/osdbase.h vdr-1.7.0-extensions/osdbase.h
+--- vdr-1.7.0/osdbase.h 2007-11-03 15:50:52.000000000 +0100
++++ vdr-1.7.0-extensions/osdbase.h 2009-04-09 20:48:48.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.7.0/osd.c vdr-1.7.0-extensions/osd.c
+--- vdr-1.7.0/osd.c 2007-10-12 14:38:36.000000000 +0200
++++ vdr-1.7.0-extensions/osd.c 2009-04-09 20:48:48.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.7.0/osd.h vdr-1.7.0-extensions/osd.h
+--- vdr-1.7.0/osd.h 2007-10-12 16:28:44.000000000 +0200
++++ vdr-1.7.0-extensions/osd.h 2009-04-09 20:48:48.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.7.0/player.c vdr-1.7.0-extensions/player.c
+--- vdr-1.7.0/player.c 2007-07-20 17:25:24.000000000 +0200
++++ vdr-1.7.0-extensions/player.c 2009-04-09 20:48:48.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.7.0/player.h vdr-1.7.0-extensions/player.h
+--- vdr-1.7.0/player.h 2008-02-16 14:50:11.000000000 +0100
++++ vdr-1.7.0-extensions/player.h 2009-04-09 20:48:48.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.7.0/plugin.c vdr-1.7.0-extensions/plugin.c
+--- vdr-1.7.0/plugin.c 2008-02-17 14:32:12.000000000 +0100
++++ vdr-1.7.0-extensions/plugin.c 2009-04-09 20:48:48.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.7.0/po/ca_ES.po vdr-1.7.0-extensions/po/ca_ES.po
+--- vdr-1.7.0/po/ca_ES.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/ca_ES.po 2009-04-09 20:48:48.000000000 +0200
+@@ -10,7 +10,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-03-02 19:02+0100\n"
+ "Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
+ "Language-Team: Catalanian\n"
+@@ -24,15 +24,12 @@ msgstr "off"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "auto"
++
+ msgid "none"
+ msgstr "cap"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Canal incorrecte ***"
+
+@@ -321,12 +318,6 @@ msgstr "Protegir"
+ msgid "Hierarchy"
+ msgstr "Jerarquia"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioritat"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -375,6 +366,9 @@ msgstr "Fi"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioritat"
++
+ msgid "Lifetime"
+ msgstr "Durada"
+
+@@ -909,9 +903,6 @@ msgstr "Sobrescriure"
+ msgid "Button$Insert"
+ msgstr "Inserir"
+
+-msgid "auto"
+-msgstr "auto"
+-
+ msgid "Plugin"
+ msgstr "Plugin"
+
+@@ -1019,3 +1010,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.7.0/po/cs_CZ.po vdr-1.7.0-extensions/po/cs_CZ.po
+--- vdr-1.7.0/po/cs_CZ.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/cs_CZ.po 2009-04-09 20:48:48.000000000 +0200
+@@ -8,7 +8,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-02-28 15:00+0200\n"
+ "Last-Translator: Vladimír Bárta <vladimir.barta@k2atmitec.cz>, Jiøí Dobrý <jdobry@centrum.cz>\n"
+ "Language-Team: Czech\n"
+@@ -22,15 +22,12 @@ msgstr "vyp."
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "auto"
++
+ msgid "none"
+ msgstr "¾ádný"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Neplatný kanál ***"
+
+@@ -319,12 +316,6 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Hierarchy"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Priorita"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -373,6 +364,9 @@ msgstr "Konec"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Priorita"
++
+ msgid "Lifetime"
+ msgstr "®ivotnost"
+
+@@ -907,9 +901,6 @@ msgstr "Pøepsat"
+ msgid "Button$Insert"
+ msgstr "Vlo¾it"
+
+-msgid "auto"
+-msgstr "auto"
+-
+ msgid "Plugin"
+ msgstr "Modul"
+
+@@ -1017,3 +1008,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.7.0/po/da_DK.po vdr-1.7.0-extensions/po/da_DK.po
+--- vdr-1.7.0/po/da_DK.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/da_DK.po 2009-04-09 20:48:48.000000000 +0200
+@@ -7,7 +7,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2007-08-12 14:17+0200\n"
+ "Last-Translator: Mogens Elneff <mogens@elneff.dk>\n"
+ "Language-Team: Danish\n"
+@@ -21,15 +21,12 @@ msgstr "fra"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "automatisk"
++
+ msgid "none"
+ msgstr "ingen"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Ugyldig kanal! ***"
+
+@@ -318,12 +315,6 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Hierarki"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioritet"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -372,6 +363,9 @@ msgstr "Stop"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioritet"
++
+ msgid "Lifetime"
+ msgstr "Levetid"
+
+@@ -906,9 +900,6 @@ msgstr "Overskriv"
+ msgid "Button$Insert"
+ msgstr "Indsæt"
+
+-msgid "auto"
+-msgstr "automatisk"
+-
+ msgid "Plugin"
+ msgstr "Plugin"
+
+@@ -1016,3 +1007,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.7.0/po/de_DE.po vdr-1.7.0-extensions/po/de_DE.po
+--- vdr-1.7.0/po/de_DE.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/de_DE.po 2009-04-09 20:48:48.000000000 +0200
+@@ -7,7 +7,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2007-11-25 15:19+0200\n"
+ "Last-Translator: Klaus Schmidinger <kls@cadsoft.de>\n"
+ "Language-Team: German\n"
+@@ -21,15 +21,12 @@ msgstr "aus"
+ msgid "on"
+ msgstr "ein"
+
++msgid "auto"
++msgstr "auto"
++
+ msgid "none"
+ msgstr "keine"
+
+-msgid "high"
+-msgstr "hoch"
+-
+-msgid "low"
+-msgstr "niedrig"
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Ungültiger Kanal ***"
+
+@@ -318,12 +315,6 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Hierarchie"
+
+-msgid "Alpha"
+-msgstr "Alpha"
+-
+-msgid "Priority"
+-msgstr "Priorität"
+-
+ msgid "Rolloff"
+ msgstr "Rolloff"
+
+@@ -372,6 +363,9 @@ msgstr "Ende"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Priorität"
++
+ msgid "Lifetime"
+ msgstr "Lebensdauer"
+
+@@ -800,6 +794,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"
+
+@@ -906,9 +924,6 @@ msgstr "Überschreiben"
+ msgid "Button$Insert"
+ msgstr "Einfügen"
+
+-msgid "auto"
+-msgstr "auto"
+-
+ msgid "Plugin"
+ msgstr "Plugin"
+
+@@ -1016,3 +1031,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.7.0/po/el_GR.po vdr-1.7.0-extensions/po/el_GR.po
+--- vdr-1.7.0/po/el_GR.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/el_GR.po 2009-04-09 20:48:48.000000000 +0200
+@@ -7,7 +7,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2007-08-12 14:17+0200\n"
+ "Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n"
+ "Language-Team: Greek\n"
+@@ -21,15 +21,12 @@ msgstr "êëåéóôü"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "áõôüìáôï"
++
+ msgid "none"
+ msgstr "êáíÝíá"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Áêõñï êáíÜëç ***"
+
+@@ -318,12 +315,6 @@ msgstr "Ðñïóôáóßá"
+ msgid "Hierarchy"
+ msgstr "Éåñáñ÷åßá"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Ðñïôåñáéüôçôá"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -372,6 +363,9 @@ msgstr "ÔÝëïò"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Ðñïôåñáéüôçôá"
++
+ msgid "Lifetime"
+ msgstr "ÄéÝñêåéá ÐáñáìïíÞò"
+
+@@ -906,9 +900,6 @@ msgstr "ÁíôéêáôÜóôáóç"
+ msgid "Button$Insert"
+ msgstr "ÅéóáãùãÞ"
+
+-msgid "auto"
+-msgstr "áõôüìáôï"
+-
+ msgid "Plugin"
+ msgstr "ÅðÝêôáóç"
+
+@@ -1016,3 +1007,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.7.0/po/es_ES.po vdr-1.7.0-extensions/po/es_ES.po
+--- vdr-1.7.0/po/es_ES.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/es_ES.po 2009-04-09 20:48:48.000000000 +0200
+@@ -8,7 +8,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-03-02 19:02+0100\n"
+ "Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
+ "Language-Team: Spanish\n"
+@@ -22,15 +22,12 @@ msgstr "off"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "auto"
++
+ msgid "none"
+ msgstr "ninguno"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Canal no válido ***"
+
+@@ -319,12 +316,6 @@ msgstr "Int.Guarda"
+ msgid "Hierarchy"
+ msgstr "Jerarquía"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioridad"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -373,6 +364,9 @@ msgstr "Fin"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioridad"
++
+ msgid "Lifetime"
+ msgstr "Duración"
+
+@@ -907,9 +901,6 @@ msgstr "Sobreescribir"
+ msgid "Button$Insert"
+ msgstr "Insertar"
+
+-msgid "auto"
+-msgstr "auto"
+-
+ msgid "Plugin"
+ msgstr "Plugin"
+
+@@ -1017,3 +1008,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.7.0/po/et_EE.po vdr-1.7.0-extensions/po/et_EE.po
+--- vdr-1.7.0/po/et_EE.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/et_EE.po 2009-04-09 20:48:48.000000000 +0200
+@@ -7,7 +7,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2007-08-12 14:17+0200\n"
+ "Last-Translator: Arthur Konovalov <kasjas@hot.ee>\n"
+ "Language-Team: Estonian\n"
+@@ -21,15 +21,12 @@ msgstr "väljas"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "automaatne"
++
+ msgid "none"
+ msgstr "puudu"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Vigane kanal ***"
+
+@@ -318,12 +315,6 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Hierarhia"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioriteet"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -372,6 +363,9 @@ msgstr "Stopp"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioriteet"
++
+ msgid "Lifetime"
+ msgstr "Eluiga"
+
+@@ -906,9 +900,6 @@ msgstr "Asenda (OVR)"
+ msgid "Button$Insert"
+ msgstr "Lisa (INS)"
+
+-msgid "auto"
+-msgstr "automaatne"
+-
+ msgid "Plugin"
+ msgstr "Laiendusmoodul"
+
+@@ -1016,3 +1007,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.7.0/po/fi_FI.po vdr-1.7.0-extensions/po/fi_FI.po
+--- vdr-1.7.0/po/fi_FI.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/fi_FI.po 2009-04-09 20:48:48.000000000 +0200
+@@ -10,7 +10,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2007-08-15 15:52+0200\n"
+ "Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
+ "Language-Team: Finnish\n"
+@@ -24,15 +24,12 @@ msgstr "poissa"
+ msgid "on"
+ msgstr "päällä"
+
++msgid "auto"
++msgstr "auto"
++
+ msgid "none"
+ msgstr "tyhjä"
+
+-msgid "high"
+-msgstr "korkea"
+-
+-msgid "low"
+-msgstr "matala"
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Virheellinen kanavavalinta ***"
+
+@@ -45,6 +42,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ä"
+
+@@ -321,12 +565,6 @@ msgstr "Suojaväli"
+ msgid "Hierarchy"
+ msgstr "Hierarkia"
+
+-msgid "Alpha"
+-msgstr "Alpha"
+-
+-msgid "Priority"
+-msgstr "Prioriteetti"
+-
+ msgid "Rolloff"
+ msgstr "Rolloff"
+
+@@ -375,6 +613,9 @@ msgstr "Lopetus"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioriteetti"
++
+ msgid "Lifetime"
+ msgstr "Elinikä"
+
+@@ -909,9 +1150,6 @@ msgstr "Korvaa"
+ msgid "Button$Insert"
+ msgstr "Lisää"
+
+-msgid "auto"
+-msgstr "auto"
+-
+ msgid "Plugin"
+ msgstr "Laajennos"
+
+@@ -1019,3 +1257,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.7.0/po/fr_FR.po vdr-1.7.0-extensions/po/fr_FR.po
+--- vdr-1.7.0/po/fr_FR.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/fr_FR.po 2009-04-09 20:48:48.000000000 +0200
+@@ -8,12 +8,13 @@
+ # 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 ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-02-27 18:14+0100\n"
+ "Last-Translator: Jean-Claude Repetto <jc@repetto.org>\n"
+ "Language-Team: French\n"
+@@ -27,15 +28,12 @@ msgstr "off"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "auto"
++
+ msgid "none"
+ msgstr "aucun"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Chaîne invalide ! ***"
+
+@@ -324,12 +322,6 @@ msgstr "Intervalle de garde"
+ msgid "Hierarchy"
+ msgstr "Hiérarchie"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Priorité"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -378,6 +370,9 @@ msgstr "Fin"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Priorité"
++
+ msgid "Lifetime"
+ msgstr "Durée de vie"
+
+@@ -912,9 +907,6 @@ msgstr "Ecraser"
+ msgid "Button$Insert"
+ msgstr "Insérer"
+
+-msgid "auto"
+-msgstr "auto"
+-
+ msgid "Plugin"
+ msgstr "Plugin"
+
+@@ -1022,3 +1014,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.7.0/po/hr_HR.po vdr-1.7.0-extensions/po/hr_HR.po
+--- vdr-1.7.0/po/hr_HR.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/hr_HR.po 2009-04-09 20:48:48.000000000 +0200
+@@ -9,7 +9,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-03-17 19:00+0100\n"
+ "Last-Translator: Adrian Caval <anrxc@sysphere.org>\n"
+ "Language-Team: Croatian\n"
+@@ -23,15 +23,12 @@ msgstr "iskljuèi"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "automatski"
++
+ msgid "none"
+ msgstr "ni¹ta"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Neispravan Program ***"
+
+@@ -320,12 +317,6 @@ msgstr "Za¹tita"
+ msgid "Hierarchy"
+ msgstr "Hijerarhija"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioritet"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -374,6 +365,9 @@ msgstr "Kraj"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioritet"
++
+ msgid "Lifetime"
+ msgstr "Trajanje"
+
+@@ -908,9 +902,6 @@ msgstr "Prepi¹i"
+ msgid "Button$Insert"
+ msgstr "Umetni"
+
+-msgid "auto"
+-msgstr "automatski"
+-
+ msgid "Plugin"
+ msgstr "Dodatak"
+
+@@ -1018,3 +1009,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.7.0/po/hu_HU.po vdr-1.7.0-extensions/po/hu_HU.po
+--- vdr-1.7.0/po/hu_HU.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/hu_HU.po 2009-04-09 20:48:48.000000000 +0200
+@@ -10,7 +10,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2007-12-01 21:42+0200\n"
+ "Last-Translator: István Füley <ifuley@tigercomp.ro>\n"
+ "Language-Team: Hungarian\n"
+@@ -24,15 +24,12 @@ msgstr "ki"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "auto"
++
+ msgid "none"
+ msgstr "semmi"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Érvénytelen csatorna ***"
+
+@@ -321,12 +318,6 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Hierarhia"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioritás"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -375,6 +366,9 @@ msgstr "Vége"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioritás"
++
+ msgid "Lifetime"
+ msgstr "Élettartam"
+
+@@ -909,9 +903,6 @@ msgstr "Átírni"
+ msgid "Button$Insert"
+ msgstr "Beilleszteni"
+
+-msgid "auto"
+-msgstr "auto"
+-
+ msgid "Plugin"
+ msgstr "Plugin"
+
+@@ -1019,3 +1010,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.7.0/po/it_IT.po vdr-1.7.0-extensions/po/it_IT.po
+--- vdr-1.7.0/po/it_IT.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/it_IT.po 2009-04-09 20:48:48.000000000 +0200
+@@ -11,10 +11,10 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
+-"PO-Revision-Date: 2008-03-08 21:06+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"
++"Language-Team: Italian\n"
+ "MIME-Version: 1.0\n"
+ "Content-Type: text/plain; charset=ISO-8859-15\n"
+ "Content-Transfer-Encoding: 8bit\n"
+@@ -25,18 +25,18 @@ msgstr "off"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "automatico"
++
+ msgid "none"
+ msgstr "nessuno"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-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!"
+
+@@ -46,6 +46,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"
+
+@@ -232,11 +479,14 @@ msgstr "Utente8"
+ msgid "Key$User9"
+ msgstr "Utente9"
+
++msgid "Recording started"
++msgstr "Registrazione avviata"
++
+ msgid "Disk"
+ msgstr "Disco"
+
+ msgid "free"
+-msgstr "liberi"
++msgstr "disponibili"
+
+ msgid "Free To Air"
+ msgstr "in chiaro"
+@@ -322,15 +572,12 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Gerarchia"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Priorità"
+-
+ msgid "Rolloff"
+ msgstr ""
+
++msgid "Parameters"
++msgstr "Parametri"
++
+ msgid "Channel settings are not unique!"
+ msgstr "Parametri canale non univoci!"
+
+@@ -376,12 +623,27 @@ msgstr "Fine"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Priorità"
++
+ msgid "Lifetime"
+-msgstr "Durata"
++msgstr "Scadenza"
++
++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"
+
+@@ -400,6 +662,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"
+
+@@ -419,20 +684,20 @@ msgid "What's on next?"
+ msgstr "Prossimi programmi"
+
+ msgid "Button$Next"
+-msgstr "Prossimo"
++msgstr "Prossimi"
+
+ msgid "Button$Now"
+ msgstr "Adesso"
+
+ msgid "Button$Schedule"
+-msgstr "Programma"
++msgstr "Programmi"
+
+ msgid "Can't switch channel!"
+ msgstr "Impossibile cambiare canale!"
+
+ #, c-format
+ msgid "Schedule - %s"
+-msgstr "Programma - %s"
++msgstr "Programmi - %s"
+
+ #, c-format
+ msgid "This event - %s"
+@@ -460,6 +725,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"
+
+@@ -469,8 +755,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?"
+@@ -481,6 +767,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"
+
+@@ -490,6 +791,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"
+
+@@ -502,6 +815,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"
+
+@@ -515,7 +831,7 @@ msgid "Setup.OSD$Height"
+ msgstr "Altezza OSD"
+
+ msgid "Setup.OSD$Message time (s)"
+-msgstr "Tempo del messaggio (s)"
++msgstr "Durata del messaggio (s)"
+
+ msgid "Setup.OSD$Use small font"
+ msgstr "Utilizza caratteri piccoli"
+@@ -571,12 +887,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)"
+
+@@ -586,8 +920,11 @@ 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 automatico"
++msgstr "Imposta orario di sistema"
+
+ msgid "Setup.EPG$Use time from transponder"
+ msgstr "Utilizza orario da transponder"
+@@ -600,6 +937,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"
+
+@@ -609,9 +967,6 @@ msgstr "letterbox"
+ msgid "center cut out"
+ msgstr "center cut out"
+
+-msgid "no"
+-msgstr "no"
+-
+ msgid "names only"
+ msgstr "solo nomi"
+
+@@ -630,6 +985,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"
+
+@@ -637,13 +1013,13 @@ msgid "Setup.DVB$Video format"
+ msgstr "Formato video"
+
+ msgid "Setup.DVB$Video display format"
+-msgstr "Formato di visualizz. video"
++msgstr "Formato visualizzazione video"
+
+ msgid "Setup.DVB$Use Dolby Digital"
+ msgstr "Dolby Digital"
+
+ msgid "Setup.DVB$Update channels"
+-msgstr "Aggiorna i canali"
++msgstr "Aggiornamento canali"
+
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Lingue audio"
+@@ -669,9 +1045,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"
+
+@@ -714,6 +1109,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"
+
+@@ -730,17 +1128,37 @@ msgid "Setup.Recording$Default priority"
+ msgstr "Priorità predefinita"
+
+ msgid "Setup.Recording$Default lifetime (d)"
+-msgstr "Durata predefinita (gg)"
++msgstr "Scadenza predefinita (gg)"
+
+ msgid "Setup.Recording$Pause priority"
+ msgstr "Priorità di pausa"
+
+ msgid "Setup.Recording$Pause lifetime (d)"
+-msgstr "Durata pausa (gg)"
++msgstr "Scadenza 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"
+
+@@ -754,14 +1172,44 @@ msgid "Setup.Recording$Name instant reco
+ msgstr "Nome reg. immediata"
+
+ msgid "Setup.Recording$Instant rec. time (min)"
+-msgstr "Tempo reg. immediata (min)"
++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"
+
+@@ -774,6 +1222,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"
+
+@@ -801,9 +1306,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"
+
+@@ -829,7 +1379,6 @@ msgstr "Programmi"
+ msgid "VDR"
+ msgstr "VDR"
+
+-#. TRANSLATORS: note the leading blank!
+ msgid " Stop replaying"
+ msgstr " Ferma riproduzione"
+
+@@ -845,7 +1394,6 @@ msgstr "Ferma"
+ msgid "Button$Resume"
+ msgstr "Riprendi"
+
+-#. TRANSLATORS: note the leading blank!
+ msgid " Cancel editing"
+ msgstr " Annulla modifiche"
+
+@@ -876,10 +1424,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!"
+
+@@ -895,9 +1455,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"
+
+@@ -910,14 +1467,23 @@ msgstr "Sovrascrivi"
+ msgid "Button$Insert"
+ msgstr "Inserisci"
+
+-msgid "auto"
+-msgstr "automatico"
++msgid "Rename$Up"
++msgstr "Su"
++
++msgid "Rename$Down"
++msgstr "Giù"
++
++msgid "Rename$Previous"
++msgstr "Precedente"
++
++msgid "Rename$Next"
++msgstr "Successivo"
+
+ msgid "Plugin"
+ msgstr "Plugin"
+
+ msgid "Up/Dn for new location - OK to move"
+-msgstr "Su/Giù per nuova posizione - OK per muovere"
++msgstr "Su/Giù per nuova posizione - OK per spostare"
+
+ msgid "Channel locked (recording)!"
+ msgstr "Canale bloccato (in registrazione)!"
+@@ -925,6 +1491,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!"
+
+@@ -936,7 +1526,7 @@ msgstr "Registrazione in corso - spegner
+
+ #, c-format
+ msgid "Recording in %ld minutes, shut down anyway?"
+-msgstr "Registrazione fra %ld minuti - spegnere comunque?"
++msgstr "Registrazione tra %ld minuti - spegnere comunque?"
+
+ msgid "shut down anyway?"
+ msgstr "spegnere comunque?"
+@@ -996,9 +1586,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"
+
+@@ -1020,3 +1607,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.7.0/po/nl_NL.po vdr-1.7.0-extensions/po/nl_NL.po
+--- vdr-1.7.0/po/nl_NL.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/nl_NL.po 2009-04-09 20:48:48.000000000 +0200
+@@ -11,7 +11,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-02-26 17:20+0100\n"
+ "Last-Translator: Johan Schuring <johan.schuring@vetteblei.nl>\n"
+ "Language-Team: Dutch\n"
+@@ -25,15 +25,12 @@ msgstr "uit"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "auto"
++
+ msgid "none"
+ msgstr "geen"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Ongeldig kanaal ***"
+
+@@ -322,12 +319,6 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Hierarchie"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioriteit"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -376,6 +367,9 @@ msgstr "Einde"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioriteit"
++
+ msgid "Lifetime"
+ msgstr "Bewaarduur"
+
+@@ -910,9 +904,6 @@ msgstr "Overschrijven"
+ msgid "Button$Insert"
+ msgstr "Invoegen"
+
+-msgid "auto"
+-msgstr "auto"
+-
+ msgid "Plugin"
+ msgstr "Plugin"
+
+@@ -1020,3 +1011,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.7.0/po/nn_NO.po vdr-1.7.0-extensions/po/nn_NO.po
+--- vdr-1.7.0/po/nn_NO.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/nn_NO.po 2009-04-09 20:48:48.000000000 +0200
+@@ -8,7 +8,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2007-08-12 14:17+0200\n"
+ "Last-Translator: Truls Slevigen <truls@slevigen.no>\n"
+ "Language-Team: Norwegian\n"
+@@ -22,13 +22,10 @@ msgstr ""
+ msgid "on"
+ msgstr ""
+
+-msgid "none"
+-msgstr ""
+-
+-msgid "high"
++msgid "auto"
+ msgstr ""
+
+-msgid "low"
++msgid "none"
+ msgstr ""
+
+ msgid "*** Invalid Channel ***"
+@@ -319,12 +316,6 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Hierarchy"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioritet"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -373,6 +364,9 @@ msgstr "Slutt"
+ msgid "VPS"
+ msgstr ""
+
++msgid "Priority"
++msgstr "Prioritet"
++
+ msgid "Lifetime"
+ msgstr "Levetid"
+
+@@ -907,9 +901,6 @@ msgstr ""
+ msgid "Button$Insert"
+ msgstr ""
+
+-msgid "auto"
+-msgstr ""
+-
+ msgid "Plugin"
+ msgstr "Plugin"
+
+@@ -1017,3 +1008,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.7.0/po/pl_PL.po vdr-1.7.0-extensions/po/pl_PL.po
+--- vdr-1.7.0/po/pl_PL.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/pl_PL.po 2009-04-09 20:48:48.000000000 +0200
+@@ -8,7 +8,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-03-09 12:59+0100\n"
+ "Last-Translator: Michael Rakowski <mrak@gmx.de>\n"
+ "Language-Team: Polish\n"
+@@ -22,15 +22,12 @@ msgstr "wy³±cz"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "auto"
++
+ msgid "none"
+ msgstr "brak"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Niepoprawny kana³ ***"
+
+@@ -319,12 +316,6 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Hierarchia"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Priorytet"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -373,6 +364,9 @@ msgstr "Koniec"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Priorytet"
++
+ msgid "Lifetime"
+ msgstr "Czas ¿ycia"
+
+@@ -907,9 +901,6 @@ msgstr "Nadpisz"
+ msgid "Button$Insert"
+ msgstr "Wstaw"
+
+-msgid "auto"
+-msgstr "auto"
+-
+ msgid "Plugin"
+ msgstr "Wtyczka"
+
+@@ -1017,3 +1008,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.7.0/po/pt_PT.po vdr-1.7.0-extensions/po/pt_PT.po
+--- vdr-1.7.0/po/pt_PT.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/pt_PT.po 2009-04-09 20:48:48.000000000 +0200
+@@ -7,7 +7,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-03-18 17:04+0100\n"
+ "Last-Translator: anonymous\n"
+ "Language-Team: Portuguese\n"
+@@ -21,15 +21,12 @@ msgstr "off"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "Automático"
++
+ msgid "none"
+ msgstr "nenhum"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Canal inválido ***"
+
+@@ -318,12 +315,6 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Hierarquia"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioridade"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -372,6 +363,9 @@ msgstr "Fim"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioridade"
++
+ msgid "Lifetime"
+ msgstr "Duração"
+
+@@ -906,9 +900,6 @@ msgstr "Substituir"
+ msgid "Button$Insert"
+ msgstr "Inserir"
+
+-msgid "auto"
+-msgstr "Automático"
+-
+ msgid "Plugin"
+ msgstr "Plugin"
+
+@@ -1016,3 +1007,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.7.0/po/ro_RO.po vdr-1.7.0-extensions/po/ro_RO.po
+--- vdr-1.7.0/po/ro_RO.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/ro_RO.po 2009-04-09 20:48:48.000000000 +0200
+@@ -8,7 +8,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-02-25 00:39+0100\n"
+ "Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n"
+ "Language-Team: Romanian\n"
+@@ -24,15 +24,12 @@ msgstr "inactiv"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "automat"
++
+ msgid "none"
+ msgstr "niciuna(ul)"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Canal invalid ***"
+
+@@ -321,12 +318,6 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Ierarhie"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioritate"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -375,6 +366,9 @@ msgstr "Sfârºit"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioritate"
++
+ msgid "Lifetime"
+ msgstr "Timp de pãstrare"
+
+@@ -909,9 +903,6 @@ msgstr "Suprascrie"
+ msgid "Button$Insert"
+ msgstr "Insereazã"
+
+-msgid "auto"
+-msgstr "automat"
+-
+ msgid "Plugin"
+ msgstr "Plugin (modul adiþional)"
+
+@@ -1019,3 +1010,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.7.0/po/ru_RU.po vdr-1.7.0-extensions/po/ru_RU.po
+--- vdr-1.7.0/po/ru_RU.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/ru_RU.po 2009-04-09 20:48:48.000000000 +0200
+@@ -8,7 +8,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-02-15 16:37+0100\n"
+ "Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
+ "Language-Team: Russian\n"
+@@ -22,15 +22,12 @@ msgstr "ÒëÚÛ"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "ÐÒâÞ"
++
+ msgid "none"
+ msgstr "ÝØçÕÓÞ"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** ½ÕßàÐÒØÛìÝëÙ ÚÐÝÐÛ ***"
+
+@@ -319,12 +316,6 @@ msgstr "·ÐéØâÐ"
+ msgid "Hierarchy"
+ msgstr "¸ÕàÐàåØï"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "¿àØÞàØâÕâ"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -373,6 +364,9 @@ msgstr "ºÞÝÕæ"
+ msgid "VPS"
+ msgstr "VPS ßÞßàÐÒÚÐ"
+
++msgid "Priority"
++msgstr "¿àØÞàØâÕâ"
++
+ msgid "Lifetime"
+ msgstr "ÁàÞÚ åàÐÝÕÝØï"
+
+@@ -907,9 +901,6 @@ msgstr "·ÐÜÕÝÐ"
+ msgid "Button$Insert"
+ msgstr "²áâÐÒÚÐ"
+
+-msgid "auto"
+-msgstr "ÐÒâÞ"
+-
+ msgid "Plugin"
+ msgstr "¼ÞÔãÛì"
+
+@@ -1017,3 +1008,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.7.0/po/sl_SI.po vdr-1.7.0-extensions/po/sl_SI.po
+--- vdr-1.7.0/po/sl_SI.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/sl_SI.po 2009-04-09 20:48:48.000000000 +0200
+@@ -8,7 +8,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-02-28 19:44+0100\n"
+ "Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n"
+ "Language-Team: Slovenian\n"
+@@ -22,15 +22,12 @@ msgstr "izklop"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "avtomatsko"
++
+ msgid "none"
+ msgstr "nobeden"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Neznan kanal ***"
+
+@@ -319,12 +316,6 @@ msgstr "Za¹èita"
+ msgid "Hierarchy"
+ msgstr "Hierarhija"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioriteta"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -373,6 +364,9 @@ msgstr "Konec"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioriteta"
++
+ msgid "Lifetime"
+ msgstr "Veljavnost"
+
+@@ -907,9 +901,6 @@ msgstr "Prepi¹i"
+ msgid "Button$Insert"
+ msgstr "Vstavi"
+
+-msgid "auto"
+-msgstr "avtomatsko"
+-
+ msgid "Plugin"
+ msgstr "Vstavek"
+
+@@ -1017,3 +1008,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.7.0/po/sv_SE.po vdr-1.7.0-extensions/po/sv_SE.po
+--- vdr-1.7.0/po/sv_SE.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/sv_SE.po 2009-04-09 20:48:48.000000000 +0200
+@@ -10,7 +10,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-03-12 18:25+0100\n"
+ "Last-Translator: Magnus Andersson <svankan@bahnhof.se>\n"
+ "Language-Team: Swedish\n"
+@@ -24,15 +24,12 @@ msgstr "av"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "automatisk"
++
+ msgid "none"
+ msgstr "ingen"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Felaktig kanal ***"
+
+@@ -321,12 +318,6 @@ msgstr "Guard"
+ msgid "Hierarchy"
+ msgstr "Hierarchy"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Prioritet"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -375,6 +366,9 @@ msgstr "Slutar"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Prioritet"
++
+ msgid "Lifetime"
+ msgstr "Livstid"
+
+@@ -909,9 +903,6 @@ msgstr "Skriv över"
+ msgid "Button$Insert"
+ msgstr "Infoga"
+
+-msgid "auto"
+-msgstr "automatisk"
+-
+ msgid "Plugin"
+ msgstr "Modul"
+
+@@ -1019,3 +1010,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.7.0/po/tr_TR.po vdr-1.7.0-extensions/po/tr_TR.po
+--- vdr-1.7.0/po/tr_TR.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/tr_TR.po 2009-04-09 20:48:48.000000000 +0200
+@@ -7,7 +7,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-02-28 00:33+0100\n"
+ "Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n"
+ "Language-Team: Turkish\n"
+@@ -21,15 +21,12 @@ msgstr "kapalý"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "otomatik"
++
+ msgid "none"
+ msgstr "hiç"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Geçersiz kanal ***"
+
+@@ -318,12 +315,6 @@ msgstr "Koruma"
+ msgid "Hierarchy"
+ msgstr "Hiyerarþi"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "Öncelik"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -372,6 +363,9 @@ msgstr "Bitiþ"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "Öncelik"
++
+ msgid "Lifetime"
+ msgstr "Ömrü"
+
+@@ -906,9 +900,6 @@ msgstr "Üstüne yaz"
+ msgid "Button$Insert"
+ msgstr "Ekle"
+
+-msgid "auto"
+-msgstr "otomatik"
+-
+ msgid "Plugin"
+ msgstr "Eklenti"
+
+@@ -1016,3 +1007,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.7.0/po/uk_UA.po vdr-1.7.0-extensions/po/uk_UA.po
+--- vdr-1.7.0/po/uk_UA.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/uk_UA.po 2009-04-09 20:48:48.000000000 +0200
+@@ -7,7 +7,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-03-07 14:17+0200\n"
+ "Last-Translator: Yarema Aka Knedlyk <yupadmin@gmail.com>\n"
+ "Language-Team: Ukrainian\n"
+@@ -21,15 +21,12 @@ msgstr "ÒØÚÛ"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "ÐÒâÞ"
++
+ msgid "none"
+ msgstr "ÝöçÞÓÞ"
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "*** ½ÕßàÐÒØÛìÝØÙ ÚÐÝÐÛ ***"
+
+@@ -318,12 +315,6 @@ msgstr "·ÐåØáâ"
+ msgid "Hierarchy"
+ msgstr "¦ôàÐàåöï"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "¿àöÞàØâÕâ"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -372,6 +363,9 @@ msgstr "ºöÝÕæì"
+ msgid "VPS"
+ msgstr "VPS ßÞßàÐÒÚÐ"
+
++msgid "Priority"
++msgstr "¿àöÞàØâÕâ"
++
+ msgid "Lifetime"
+ msgstr "ÁâàÞÚ ×ÑÕàöÓÐÝÝï"
+
+@@ -906,9 +900,6 @@ msgstr "·ÐÜöÝÐ"
+ msgid "Button$Insert"
+ msgstr "²áâÐÒÚÐ"
+
+-msgid "auto"
+-msgstr "ÐÒâÞ"
+-
+ msgid "Plugin"
+ msgstr "¼ÞÔãÛì"
+
+diff -ruNp vdr-1.7.0/po/zh_CN.po vdr-1.7.0-extensions/po/zh_CN.po
+--- vdr-1.7.0/po/zh_CN.po 2008-04-13 16:16:58.000000000 +0200
++++ vdr-1.7.0-extensions/po/zh_CN.po 2009-04-09 20:48:48.000000000 +0200
+@@ -7,7 +7,7 @@ msgid ""
+ msgstr ""
+ "Project-Id-Version: VDR 1.6.0\n"
+ "Report-Msgid-Bugs-To: <vdr-bugs@cadsoft.de>\n"
+-"POT-Creation-Date: 2008-04-12 14:19+0200\n"
++"POT-Creation-Date: 2008-12-14 16:10+0100\n"
+ "PO-Revision-Date: 2008-03-21 08:44+0800\n"
+ "Last-Translator: Nan Feng <nfgx@21cn.com>\n"
+ "Language-Team: Chinese\n"
+@@ -24,15 +24,12 @@ msgstr "å…³"
+ msgid "on"
+ msgstr ""
+
++msgid "auto"
++msgstr "自动"
++
+ msgid "none"
+ msgstr "æ— "
+
+-msgid "high"
+-msgstr ""
+-
+-msgid "low"
+-msgstr ""
+-
+ msgid "*** Invalid Channel ***"
+ msgstr "***无效频� ***"
+
+@@ -321,12 +318,6 @@ msgstr "防护"
+ msgid "Hierarchy"
+ msgstr "层次"
+
+-msgid "Alpha"
+-msgstr ""
+-
+-msgid "Priority"
+-msgstr "优先"
+-
+ msgid "Rolloff"
+ msgstr ""
+
+@@ -375,6 +366,9 @@ msgstr "�止"
+ msgid "VPS"
+ msgstr "VPS"
+
++msgid "Priority"
++msgstr "优先"
++
+ msgid "Lifetime"
+ msgstr "终生"
+
+@@ -909,9 +903,6 @@ msgstr "覆盖"
+ msgid "Button$Insert"
+ msgstr "�入"
+
+-msgid "auto"
+-msgstr "自动"
+-
+ msgid "Plugin"
+ msgstr "�件"
+
+diff -ruNp vdr-1.7.0/README.cmdsubmenu vdr-1.7.0-extensions/README.cmdsubmenu
+--- vdr-1.7.0/README.cmdsubmenu 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/README.cmdsubmenu 2009-04-09 20:48:48.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.7.0/README-HLCUTTER vdr-1.7.0-extensions/README-HLCUTTER
+--- vdr-1.7.0/README-HLCUTTER 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/README-HLCUTTER 2009-04-09 20:48:48.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.7.0/README.jumpplay vdr-1.7.0-extensions/README.jumpplay
+--- vdr-1.7.0/README.jumpplay 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/README.jumpplay 2009-04-09 20:48:48.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.7.0/README.MainMenuHooks vdr-1.7.0-extensions/README.MainMenuHooks
+--- vdr-1.7.0/README.MainMenuHooks 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/README.MainMenuHooks 2009-04-09 20:48:48.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.7.0/README.sortrec vdr-1.7.0-extensions/README.sortrec
+--- vdr-1.7.0/README.sortrec 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/README.sortrec 2009-04-09 20:48:48.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.7.0/README.timer-info vdr-1.7.0-extensions/README.timer-info
+--- vdr-1.7.0/README.timer-info 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/README.timer-info 2009-04-09 20:48:48.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.7.0/recorder.c vdr-1.7.0-extensions/recorder.c
+--- vdr-1.7.0/recorder.c 2007-02-24 17:36:24.000000000 +0100
++++ vdr-1.7.0-extensions/recorder.c 2009-04-09 20:48:48.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.7.0/recorder.h vdr-1.7.0-extensions/recorder.h
+--- vdr-1.7.0/recorder.h 2007-01-05 11:44:05.000000000 +0100
++++ vdr-1.7.0-extensions/recorder.h 2009-04-09 20:48:48.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.7.0/recording.c vdr-1.7.0-extensions/recording.c
+--- vdr-1.7.0/recording.c 2008-02-24 11:28:53.000000000 +0100
++++ vdr-1.7.0-extensions/recording.c 2009-04-09 20:48:48.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.7.0/recording.h vdr-1.7.0-extensions/recording.h
+--- vdr-1.7.0/recording.h 2007-10-14 12:11:34.000000000 +0200
++++ vdr-1.7.0-extensions/recording.h 2009-04-09 20:48:48.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.7.0/remux.c vdr-1.7.0-extensions/remux.c
+--- vdr-1.7.0/remux.c 2007-11-25 14:56:03.000000000 +0100
++++ vdr-1.7.0-extensions/remux.c 2009-04-09 20:48:48.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.7.0/remux.h vdr-1.7.0-extensions/remux.h
+--- vdr-1.7.0/remux.h 2007-09-02 12:19:06.000000000 +0200
++++ vdr-1.7.0-extensions/remux.h 2009-04-09 20:48:48.000000000 +0200
+@@ -40,6 +40,9 @@ private:
+ bool noVideo;
+ int numUPTerrors;
+ bool synced;
++#ifdef USE_SYNCEARLY
++ bool syncEarly;
++#endif /* SYNCEARLY */
+ int skipped;
+ cTS2PES *ts2pes[MAXTRACKS];
+ int numTracks;
+@@ -47,12 +50,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.7.0/skinclassic.c vdr-1.7.0-extensions/skinclassic.c
+--- vdr-1.7.0/skinclassic.c 2008-02-23 11:31:58.000000000 +0100
++++ vdr-1.7.0-extensions/skinclassic.c 2009-04-09 20:48:48.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.7.0/skinsttng.c vdr-1.7.0-extensions/skinsttng.c
+--- vdr-1.7.0/skinsttng.c 2008-02-23 11:23:44.000000000 +0100
++++ vdr-1.7.0-extensions/skinsttng.c 2009-04-09 20:48:48.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.7.0/sources.c vdr-1.7.0-extensions/sources.c
+--- vdr-1.7.0/sources.c 2008-02-10 15:07:26.000000000 +0100
++++ vdr-1.7.0-extensions/sources.c 2009-04-09 20:48:48.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.7.0/sources.conf vdr-1.7.0-extensions/sources.conf
+--- vdr-1.7.0/sources.conf 2007-02-17 17:15:13.000000000 +0100
++++ vdr-1.7.0-extensions/sources.conf 2009-04-09 20:48:48.000000000 +0200
+@@ -188,3 +188,7 @@ C Cable
+ # Terrestrial
+
+ T Terrestrial
++
++# Plugin PLUGINPARAM
++
++#P Plugin
+diff -ruNp vdr-1.7.0/sources.h vdr-1.7.0-extensions/sources.h
+--- vdr-1.7.0/sources.h 2005-05-14 11:30:41.000000000 +0200
++++ vdr-1.7.0-extensions/sources.h 2009-04-09 20:48:48.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.7.0/status.c vdr-1.7.0-extensions/status.c
+--- vdr-1.7.0/status.c 2008-02-16 15:46:31.000000000 +0100
++++ vdr-1.7.0-extensions/status.c 2009-04-09 20:48:48.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,88 @@ 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.7.0/status.h vdr-1.7.0-extensions/status.h
+--- vdr-1.7.0/status.h 2008-02-16 16:00:33.000000000 +0100
++++ vdr-1.7.0-extensions/status.h 2009-04-09 20:48:48.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,46 @@ 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 +152,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.7.0/submenu.c vdr-1.7.0-extensions/submenu.c
+--- vdr-1.7.0/submenu.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/submenu.c 2009-04-09 20:48:48.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.7.0/submenu.h vdr-1.7.0-extensions/submenu.h
+--- vdr-1.7.0/submenu.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/submenu.h 2009-04-09 20:48:48.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.7.0/svdrp.c vdr-1.7.0-extensions/svdrp.c
+--- vdr-1.7.0/svdrp.c 2008-02-17 14:36:01.000000000 +0100
++++ vdr-1.7.0-extensions/svdrp.c 2009-04-09 20:48:48.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.7.0/svdrp.h vdr-1.7.0-extensions/svdrp.h
+--- vdr-1.7.0/svdrp.h 2007-04-30 14:28:28.000000000 +0200
++++ vdr-1.7.0-extensions/svdrp.h 2009-04-09 20:48:48.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.7.0/timers.c vdr-1.7.0-extensions/timers.c
+--- vdr-1.7.0/timers.c 2008-04-13 14:41:41.000000000 +0200
++++ vdr-1.7.0-extensions/timers.c 2009-04-09 20:48:48.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.7.0/timers.h vdr-1.7.0-extensions/timers.h
+--- vdr-1.7.0/timers.h 2008-02-16 15:33:23.000000000 +0100
++++ vdr-1.7.0-extensions/timers.h 2009-04-09 20:48:48.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.7.0/tinystr.c vdr-1.7.0-extensions/tinystr.c
+--- vdr-1.7.0/tinystr.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/tinystr.c 2009-04-09 20:48:48.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.7.0/tinystr.h vdr-1.7.0-extensions/tinystr.h
+--- vdr-1.7.0/tinystr.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/tinystr.h 2009-04-09 20:48:48.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.7.0/tinyxml.c vdr-1.7.0-extensions/tinyxml.c
+--- vdr-1.7.0/tinyxml.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/tinyxml.c 2009-04-09 20:48:48.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.7.0/tinyxmlerror.c vdr-1.7.0-extensions/tinyxmlerror.c
+--- vdr-1.7.0/tinyxmlerror.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/tinyxmlerror.c 2009-04-09 20:48:48.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.7.0/tinyxml.h vdr-1.7.0-extensions/tinyxml.h
+--- vdr-1.7.0/tinyxml.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/tinyxml.h 2009-04-09 20:48:48.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.7.0/tinyxmlparser.c vdr-1.7.0-extensions/tinyxmlparser.c
+--- vdr-1.7.0/tinyxmlparser.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/tinyxmlparser.c 2009-04-09 20:48:48.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.7.0/transfer.c vdr-1.7.0-extensions/transfer.c
+--- vdr-1.7.0/transfer.c 2007-01-05 11:45:28.000000000 +0100
++++ vdr-1.7.0-extensions/transfer.c 2009-04-09 20:48:48.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.7.0/transfer.h vdr-1.7.0-extensions/transfer.h
+--- vdr-1.7.0/transfer.h 2007-01-05 11:45:45.000000000 +0100
++++ vdr-1.7.0-extensions/transfer.h 2009-04-09 20:48:48.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.7.0/vdr.c vdr-1.7.0-extensions/vdr.c
+--- vdr-1.7.0/vdr.c 2008-03-14 14:22:39.000000000 +0100
++++ vdr-1.7.0-extensions/vdr.c 2009-04-09 20:48:48.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,11 +142,18 @@ static bool SetKeepCaps(bool On)
+ return true;
+ }
+
++#ifdef USE_SETTIME
++char *SetTime=NULL;
++#endif /* SETTIME */
++
+ static void SignalHandler(int signum)
+ {
+ isyslog("caught signal %d", signum);
+ switch (signum) {
+ case SIGPIPE:
++#ifdef USE_EM84XX
++ case SIGINT:
++#endif /* EM84XX */
+ break;
+ case SIGHUP:
+ LastSignal = signum;
+@@ -164,6 +174,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:
+@@ -188,8 +202,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;
+@@ -221,10 +238,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' },
+@@ -239,6 +260,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' },
+@@ -249,10 +273,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;
+@@ -266,6 +308,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);
+@@ -337,6 +382,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);
+@@ -397,11 +446,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"
+@@ -432,6 +486,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"
+@@ -475,6 +532,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)
+@@ -554,6 +818,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)
+@@ -567,8 +836,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)
+@@ -731,7 +1012,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...
+@@ -787,8 +1072,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;
+ }
+@@ -903,9 +1197,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)
+@@ -922,9 +1226,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;
+ }
+@@ -957,12 +1269,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;
+@@ -974,14 +1295,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);
+ }
+@@ -993,8 +1325,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;
+@@ -1024,8 +1365,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();
+@@ -1048,7 +1394,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!"));
+@@ -1057,9 +1407,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;
+@@ -1109,14 +1470,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;
+@@ -1153,6 +1542,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);
+@@ -1167,13 +1562,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:
+@@ -1184,6 +1592,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:
+@@ -1191,13 +1603,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();
+@@ -1267,6 +1688,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.7.0/vdrttxtsubshooks.c vdr-1.7.0-extensions/vdrttxtsubshooks.c
+--- vdr-1.7.0/vdrttxtsubshooks.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/vdrttxtsubshooks.c 2009-04-09 20:48:48.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.7.0/vdrttxtsubshooks.h vdr-1.7.0-extensions/vdrttxtsubshooks.h
+--- vdr-1.7.0/vdrttxtsubshooks.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.0-extensions/vdrttxtsubshooks.h 2009-04-09 20:48:48.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.7.0/videodir.c vdr-1.7.0-extensions/videodir.c
+--- vdr-1.7.0/videodir.c 2008-02-16 14:00:03.000000000 +0100
++++ vdr-1.7.0-extensions/videodir.c 2009-04-09 20:48:48.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.7.0/videodir.h vdr-1.7.0-extensions/videodir.h
+--- vdr-1.7.0/videodir.h 2008-02-16 13:53:11.000000000 +0100
++++ vdr-1.7.0-extensions/videodir.h 2009-04-09 20:48:48.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);
diff --git a/vdr/extensions/vdr-1.7.5_extensions.diff b/vdr/extensions/vdr-1.7.5_extensions.diff
new file mode 100644
index 0000000..76341a9
--- /dev/null
+++ b/vdr/extensions/vdr-1.7.5_extensions.diff
@@ -0,0 +1,23830 @@
+diff -ruNp vdr-1.7.5/channels.c vdr-1.7.5-extensions/channels.c
+--- vdr-1.7.5/channels.c 2009-04-10 13:29:55.000000000 +0200
++++ vdr-1.7.5-extensions/channels.c 2009-04-12 13:37:59.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
+@@ -188,6 +191,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 = 8000000;
+@@ -211,6 +217,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;
+@@ -239,6 +248,9 @@ cChannel::~cChannel()
+ free(shortName);
+ free(provider);
+ free(portalName);
++#ifdef USE_PLUGINPARAM
++ free(pluginParam);
++#endif /* PLUGINPARAM */
+ }
+
+ cChannel& cChannel::operator= (const cChannel &Channel)
+@@ -247,6 +259,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;
+ }
+@@ -306,6 +321,9 @@ void cChannel::CopyTransponderData(const
+ guard = Channel->guard;
+ hierarchy = Channel->hierarchy;
+ rollOff = Channel->rollOff;
++#ifdef USE_PLUGINPARAM
++ if (IsPlug()) pluginParam = strcpyrealloc(pluginParam, Channel->pluginParam);
++#endif /* PLUGINPARAM */
+ }
+ }
+
+@@ -343,6 +361,24 @@ bool cChannel::SetSatTransponderData(int
+ return true;
+ }
+
++#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::SetCableTransponderData(int Source, int Frequency, int Modulation, int Srate, int CoderateH)
+ {
+ if (source != Source || frequency != Frequency || modulation != Modulation || srate != Srate || coderateH != CoderateH) {
+@@ -413,6 +449,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();
+ }
+@@ -438,6 +477,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
+
+@@ -632,7 +685,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);
+@@ -646,6 +703,9 @@ cString cChannel::ParametersToString(voi
+ ST(" S ") q += PrintParameter(q, 'S', MapToUser(system, SystemValues));
+ ST(" T") q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues));
+ ST(" T") q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues));
++#ifdef USE_PLUGINPARAM
++ ST("P ") snprintf(buffer, sizeof(buffer), "%s", pluginParam);
++#endif /* PLUGINPARAM */
+ return buffer;
+ }
+
+@@ -674,7 +734,11 @@ static const char *SkipDigits(const char
+
+ bool cChannel::StringToParameters(const char *s)
+ {
++#ifdef USE_PLUGINPARAM
++ while (s && *s && !IsPlug()) {
++#else
+ while (s && *s) {
++#endif /* PLUGINPARAM */
+ switch (toupper(*s)) {
+ case 'A': s = SkipDigits(s); break; // for compatibility with the "multiproto" approach - may be removed in future versions
+ case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break;
+@@ -792,7 +856,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;
+ if ((p = strchr(vpidbuf, '=')) != NULL) {
+@@ -887,6 +955,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);
+@@ -1132,6 +1203,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.7.5/channels.h vdr-1.7.5-extensions/channels.h
+--- vdr-1.7.5/channels.h 2008-11-22 14:35:52.000000000 +0100
++++ vdr-1.7.5-extensions/channels.h 2009-04-12 13:37:59.000000000 +0200
+@@ -116,6 +116,9 @@ private:
+ char *shortName;
+ char *provider;
+ char *portalName;
++#ifdef USE_PLUGINPARAM
++ char *pluginParam;
++#endif /* PLUGINPARAM */
+ int __BeginData__;
+ int frequency; // MHz
+ int source;
+@@ -171,6 +174,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; }
+@@ -208,6 +214,9 @@ public:
+ int RollOff(void) const { return rollOff; }
+ 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); }
+@@ -215,12 +224,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, int Modulation, int System, int RollOff);
+ 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 Vtype, 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.7.5/config.c vdr-1.7.5-extensions/config.c
+--- vdr-1.7.5/config.c 2009-01-24 16:05:32.000000000 +0100
++++ vdr-1.7.5-extensions/config.c 2009-04-12 13:37:59.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,15 @@ 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 */
+ ChannelInfoPos = 0;
+ ChannelInfoTime = 5;
+ OSDLeft = 54;
+@@ -276,24 +342,135 @@ cSetup::cSetup(void)
+ FontSmlSize = 18;
+ FontFixSize = 20;
+ MaxVideoFileSize = MAXVIDEOFILESIZEDEFAULT;
++#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_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 +561,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 +661,18 @@ 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 */
+ 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 +689,144 @@ 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_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 +835,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 +885,15 @@ 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 */
+ Store("ChannelInfoPos", ChannelInfoPos);
+ Store("ChannelInfoTime", ChannelInfoTime);
+ Store("OSDLeft", OSDLeft);
+@@ -532,25 +910,156 @@ 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_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.7.5/config.h vdr-1.7.5-extensions/config.h
+--- vdr-1.7.5/config.h 2009-01-30 17:05:34.000000000 +0100
++++ vdr-1.7.5-extensions/config.h 2009-04-12 13:37:59.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 123
++#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;
+@@ -158,7 +224,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:
+@@ -167,6 +240,9 @@ public:
+
+ extern cCommands Commands;
+ extern cCommands RecordingCommands;
++#ifdef USE_TIMERCMD
++extern cCommands TimerCommands;
++#endif /* TIMERCMD */
+ extern cSVDRPhosts SVDRPhosts;
+
+ class cSetupLine : public cListObject {
+@@ -192,6 +268,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);
+@@ -201,6 +281,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;
+@@ -241,6 +327,14 @@ 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 */
+ int ChannelInfoPos;
+ int ChannelInfoTime;
+ int OSDLeft, OSDTop, OSDWidth, OSDHeight;
+@@ -254,20 +348,111 @@ 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_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);
+@@ -275,4 +460,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.7.5/cutter.c vdr-1.7.5-extensions/cutter.c
+--- vdr-1.7.5/cutter.c 2009-01-24 16:19:26.000000000 +0100
++++ vdr-1.7.5-extensions/cutter.c 2009-04-12 13:37:59.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;
+@@ -66,6 +79,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();
+@@ -77,6 +106,9 @@ void cCuttingThread::Action(void)
+ Mark = fromMarks.Next(Mark);
+ off_t FileSize = 0;
+ int CurrentFileNumber = 0;
++#ifdef USE_HARDLINKCUTTER
++ bool SkipThisSourceFile = false;
++#endif /* HARDLINKCUTTER */
+ int LastIFrame = 0;
+ toMarks.Add(0);
+ toMarks.Save();
+@@ -95,12 +127,101 @@ void cCuttingThread::Action(void)
+
+ // Read one frame:
+
++#ifndef USE_HARDLINKCUTTER
+ if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) {
+ if (FileNumber != CurrentFileNumber) {
+ fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
+ fromFile->SetReadAhead(MEGABYTE(20));
+ CurrentFileNumber = FileNumber;
+ }
++#else
++ if (!fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) {
++ // Error, unless we're past last cut-in and there's no cut-out
++ if (Mark || LastMark)
++ 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
++
++ uint16_t MarkFileNumber;
++ off_t 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) {
+@@ -117,6 +238,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)
+@@ -124,12 +246,17 @@ void cCuttingThread::Action(void)
+ break;
+ }
+
++#endif /* HARDLINKCUTTER */
+ // Write one frame:
+
+ if (Independent) { // every file shall start with an independent frame
+ if (LastMark) // edited version shall end before next I-frame
+ break;
++#ifndef USE_HARDLINKCUTTER
+ if (FileSize > maxVideoFileSize) {
++#else
++ if (!SkipThisSourceFile && FileSize > toFileName->MaxFileSize()) {
++#endif /* HARDLINKCUTTER */
+ toFile = toFileName->NextFile();
+ if (!toFile) {
+ error = "toFile 1";
+@@ -139,12 +266,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;
+ }
+@@ -179,8 +314,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();
+ }
+@@ -190,18 +361,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 + (int(First->position / Recording.FramesPerSecond() +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:
+@@ -227,6 +454,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;
+@@ -238,11 +468,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;
+@@ -253,12 +492,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;
+@@ -266,6 +533,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.7.5/cutter.h vdr-1.7.5-extensions/cutter.h
+--- vdr-1.7.5/cutter.h 2002-06-22 12:03:15.000000000 +0200
++++ vdr-1.7.5-extensions/cutter.h 2009-04-12 13:37:59.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.7.5/device.c vdr-1.7.5-extensions/device.c
+--- vdr-1.7.5/device.c 2009-04-05 14:15:41.000000000 +0200
++++ vdr-1.7.5-extensions/device.c 2009-04-12 13:37:59.000000000 +0200
+@@ -19,6 +19,14 @@
+ #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 ---------------------------------------------------------
+
+ class cLiveSubtitle : public cReceiver {
+@@ -68,6 +76,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;
+
+@@ -93,8 +107,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!");
+ }
+@@ -129,6 +150,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) {
+@@ -189,6 +220,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;
+@@ -238,6 +361,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
+@@ -271,7 +398,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];
+@@ -312,6 +463,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;
+@@ -556,7 +719,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;
+@@ -584,6 +751,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
+@@ -613,7 +798,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;
+ }
+@@ -634,6 +826,13 @@ 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 */
++
+ if (LiveView) {
+ StopReplay();
+ DELETENULL(liveSubtitle);
+@@ -646,11 +845,37 @@ eSetChannelResult cDevice::SetChannel(co
+
+ 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()!
+ cControl::Launch(new cTransferControl(Device, Channel->GetChannelID(), Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
+@@ -668,6 +893,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)
+@@ -960,7 +1189,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:
+@@ -1192,7 +1426,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.7.5/device.h vdr-1.7.5-extensions/device.h
+--- vdr-1.7.5/device.h 2009-04-05 14:12:44.000000000 +0200
++++ vdr-1.7.5-extensions/device.h 2009-04-12 13:37:59.000000000 +0200
+@@ -24,13 +24,22 @@
+ #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
+ #define VOLUMEDELTA 5 // used to increase/decrease the volume
+
++#ifdef USE_CHANNELSCAN
++extern bool scanning_on_receiving_device;
++#endif /* CHANNELSCAN */
++
+ enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
+
+ enum ePlayMode { pmNone, // audio/video from decoder
+@@ -142,6 +151,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).
+@@ -152,6 +190,9 @@ private:
+ static int nextCardIndex;
+ int cardIndex;
+ protected:
++#ifdef USE_SOURCECAPS
++ int sourceCaps[MAXSOURCECAPS];
++#endif /* SOURCECAPS */
+ cDevice(void);
+ virtual ~cDevice();
+ virtual bool Ready(void);
+@@ -239,17 +280,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; }
+@@ -267,6 +321,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.7.5/dvbdevice.c vdr-1.7.5-extensions/dvbdevice.c
+--- vdr-1.7.5/dvbdevice.c 2009-04-10 11:54:24.000000000 +0200
++++ vdr-1.7.5-extensions/dvbdevice.c 2009-04-12 13:37:59.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_delivery_system FrontendType)
+ {
+ fd_frontend = Fd_Frontend;
++#ifdef USE_ROTOR
++ SendDiseqc = false;
++#endif /* ROTOR */
+ cardIndex = CardIndex;
+ frontendType = FrontendType;
+ tuneTimeout = 0;
+@@ -163,6 +175,19 @@ bool cDvbTuner::Locked(int TimeoutMs)
+ return tunerStatus >= tsLocked;
+ }
+
++#ifdef USE_ROTOR
++bool cDvbTuner::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
++{
++ cMutexLock MutexLock(&mutex);
++ if ((!SYS_DVBS & !SYS_DVBS2) || 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) {
+@@ -241,6 +266,9 @@ bool cDvbTuner::SetFrontend(void)
+ }
+ }
+ diseqcCommands = diseqc->Commands();
++#ifdef USE_DVBSETUP
++ isyslog("Sent DISEQC command: %s", diseqcCommands);
++#endif /* DVBSETUP */
+ }
+ frequency -= diseqc->Lof();
+ }
+@@ -303,6 +331,18 @@ bool cDvbTuner::SetFrontend(void)
+ tuneTimeout = DVBC_TUNE_TIMEOUT;
+ lockTimeout = DVBC_LOCK_TIMEOUT;
+ }
++#ifdef USE_ATSC
++ else if (frontendType == SYS_ATSC) {
++ // ATSC
++ SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
++ SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
++ SETCMD(DTV_INVERSION, channel.Inversion());
++ SETCMD(DTV_MODULATION, channel.Modulation());
++
++ tuneTimeout = DVBT_TUNE_TIMEOUT;
++ lockTimeout = DVBT_LOCK_TIMEOUT;
++ }
++#endif /* ATSC */
+ else if (frontendType == SYS_DVBT) {
+ // DVB-T
+ SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
+@@ -341,6 +381,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;
+@@ -483,6 +529,11 @@ cDvbDevice::cDvbDevice(int n)
+
+ if (fd_frontend >= 0) {
+ if (ioctl(fd_frontend, FE_GET_INFO, &frontendInfo) >= 0) {
++#ifdef USE_DVBSETUP
++ if (Setup.ChannelBlockerMode == 4)
++ frontendType = (n == Setup.PrimaryDVB - 1) ? SYS_UNDEFINED : frontendType;
++ else
++#endif /* DVBSETUP */
+ switch (frontendInfo.type) {
+ case FE_QPSK: frontendType = (frontendInfo.caps & FE_CAN_2G_MODULATION) ? SYS_DVBS2 : SYS_DVBS; break;
+ case FE_OFDM: frontendType = SYS_DVBT; break;
+@@ -748,6 +799,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;
+@@ -853,14 +911,43 @@ 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 == SYS_DVBS || frontendType == SYS_DVBS2)) {
++ 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 == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B)
+ || type == cSource::stSat && (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)
++#ifdef USE_ATSC
++ || type == cSource::stTerr && (frontendType == SYS_DVBT || frontendType == SYS_ATSC);
++#else
+ || type == cSource::stTerr && (frontendType == SYS_DVBT);
++#endif /* ATSC */
+ }
+
+ 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()) && 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 */
+ if (!ProvidesSource(Channel->Source()))
+ return false; // doesn't provide source
+ if (!cSource::IsSat(Channel->Source()))
+@@ -872,10 +959,31 @@ bool cDvbDevice::ProvidesTransponder(con
+
+ 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()) && 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 (ProvidesTransponder(Channel)) {
+ result = hasPriority;
+ if (Priority >= 0 && Receiving(true)) {
+@@ -992,6 +1100,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.7.5/dvbdevice.h vdr-1.7.5-extensions/dvbdevice.h
+--- vdr-1.7.5/dvbdevice.h 2008-12-06 14:31:12.000000000 +0100
++++ vdr-1.7.5-extensions/dvbdevice.h 2009-04-12 13:37:59.000000000 +0200
+@@ -74,6 +74,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.7.5/dvbosd.c vdr-1.7.5-extensions/dvbosd.c
+--- vdr-1.7.5/dvbosd.c 2007-09-16 10:55:54.000000000 +0200
++++ vdr-1.7.5-extensions/dvbosd.c 2009-04-12 13:37:59.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.7.5/dvbplayer.c vdr-1.7.5-extensions/dvbplayer.c
+--- vdr-1.7.5/dvbplayer.c 2009-04-05 15:04:33.000000000 +0200
++++ vdr-1.7.5-extensions/dvbplayer.c 2009-04-12 13:37:59.000000000 +0200
+@@ -204,6 +204,9 @@ private:
+ cNonBlockingFileReader *nonBlockingFileReader;
+ cRingBufferFrame *ringBuffer;
+ cPtsIndex ptsIndex;
++#ifdef USE_JUMPPLAY
++ cMarksReload marks;
++#endif /* JUMPPLAY */
+ cFileName *fileName;
+ cIndexFile *index;
+ cUnbufferedFile *replayFile;
+@@ -249,7 +252,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;
+@@ -357,6 +364,11 @@ bool cDvbPlayer::Save(void)
+ if (index) {
+ int Index = ptsIndex.FindIndex(DeviceGetSTC());
+ 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 -= int(round(RESUMEBACKUP * framesPerSecond));
+ if (Index > 0)
+ Index = index->GetNextIFrame(Index, false);
+@@ -384,11 +396,29 @@ 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, framesPerSecond));
+
++#ifdef USE_JUMPPLAY
++ if (Setup.PlayJump && readIndex <= 0 && marks.First() && index) {
++ int Index = marks.First()->position;
++ uint16_t FileNumber;
++ off_t 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;
+@@ -413,7 +443,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 (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) {
+@@ -454,6 +488,42 @@ void cDvbPlayer::Action(void)
+ readIndex++;
+ else
+ eof = true;
++#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
++ bool Independent;
++ if (index->Get(readIndex, &FileNumber, &FileOffset, &Independent) && Independent)
++ total = index->Last();
++#endif /* JUMPPLAY */
+ }
+ else // allows replay even if the index file is missing
+ Length = MAXFRAMESIZE / TS_SIZE * TS_SIZE;// FIXME: use a linear ringbuffer in this case, and fix cDevice::PlayPes()
+@@ -491,6 +561,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;
+ }
+@@ -546,8 +622,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 */
+
+ // Handle hitting begin/end of recording:
+
+diff -ruNp vdr-1.7.5/eit.c vdr-1.7.5-extensions/eit.c
+--- vdr-1.7.5/eit.c 2009-04-11 12:03:24.000000000 +0200
++++ vdr-1.7.5-extensions/eit.c 2009-04-12 13:37:59.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;
+@@ -77,8 +112,74 @@ cEIT::cEIT(cSchedules *Schedules, int So
+ // not be overwritten.
+ uchar TableID = pEvent->TableID();
+ if (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.
+@@ -103,7 +204,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)
+@@ -149,8 +254,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;
+@@ -261,6 +404,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 (Tid == 0x4E) {
+ if (Empty && getSectionNumber() == 0)
+@@ -299,10 +498,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.7.5/eitscan.c vdr-1.7.5-extensions/eitscan.c
+--- vdr-1.7.5/eitscan.c 2006-01-07 15:10:17.000000000 +0100
++++ vdr-1.7.5-extensions/eitscan.c 2009-04-12 13:37:59.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.7.5/epg.c vdr-1.7.5-extensions/epg.c
+--- vdr-1.7.5/epg.c 2008-05-01 16:53:55.000000000 +0200
++++ vdr-1.7.5-extensions/epg.c 2009-04-12 13:37:59.000000000 +0200
+@@ -114,6 +114,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();
+ }
+@@ -202,6 +207,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;
+@@ -257,6 +275,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)) {
+@@ -744,6 +1096,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.7.5/epg.h vdr-1.7.5-extensions/epg.h
+--- vdr-1.7.5/epg.h 2006-10-07 15:47:19.000000000 +0200
++++ vdr-1.7.5-extensions/epg.h 2009-04-12 13:37:59.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.7.5/HISTORY-liemikuutio vdr-1.7.5-extensions/HISTORY-liemikuutio
+--- vdr-1.7.5/HISTORY-liemikuutio 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/HISTORY-liemikuutio 2009-04-12 13:37:59.000000000 +0200
+@@ -0,0 +1,116 @@
++-----------------------------------
++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).
++
++2009-01-08: Version 1.22
++
++- Updated for vdr-1.7.3.
++
++2009-01-25: Version 1.23
++
++- Updated for vdr-1.7.4.
+diff -ruNp vdr-1.7.5/iconpatch.c vdr-1.7.5-extensions/iconpatch.c
+--- vdr-1.7.5/iconpatch.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/iconpatch.c 2009-04-12 13:37:59.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.7.5/iconpatch.h vdr-1.7.5-extensions/iconpatch.h
+--- vdr-1.7.5/iconpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/iconpatch.h 2009-04-12 13:37:59.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.7.5/keys.h vdr-1.7.5-extensions/keys.h
+--- vdr-1.7.5/keys.h 2007-08-26 14:34:50.000000000 +0200
++++ vdr-1.7.5-extensions/keys.h 2009-04-12 13:37:59.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.7.5/lirc.c vdr-1.7.5-extensions/lirc.c
+--- vdr-1.7.5/lirc.c 2006-05-28 10:48:13.000000000 +0200
++++ vdr-1.7.5-extensions/lirc.c 2009-04-12 13:37:59.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.7.5/mainmenuitemsprovider.h vdr-1.7.5-extensions/mainmenuitemsprovider.h
+--- vdr-1.7.5/mainmenuitemsprovider.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/mainmenuitemsprovider.h 2009-04-12 13:37:59.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.7.5/Make.config.template vdr-1.7.5-extensions/Make.config.template
+--- vdr-1.7.5/Make.config.template 2009-01-18 11:46:13.000000000 +0100
++++ vdr-1.7.5-extensions/Make.config.template 2009-04-12 13:37:59.000000000 +0200
+@@ -42,8 +42,242 @@ 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
++#DVBSETUP = 1
++#DVDARCHIVE = 1
++#DVDCHAPJUMP = 1
++#DVLFRIENDLYFNAMES = 1
++#DVLRECSCRIPTADDON = 1
++#DVLVIDPREFER = 1
++#EM84XX = 1
++#GRAPHTFT = 1
++#HARDLINKCUTTER = 1
++#JUMPPLAY = 1
++LIEMIEXT = 1
++#LIRCSETTINGS = 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
++#TIMERCMD = 1
++#TIMERINFO = 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 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 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 STREAMDEVEXT
++DEFINES += -DUSE_STREAMDEVEXT
++endif
++
++ifdef TIMERCMD
++DEFINES += -DUSE_TIMERCMD
++endif
++
++ifdef TIMERINFO
++DEFINES += -DUSE_TIMERINFO
++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.7.5/Makefile vdr-1.7.5-extensions/Makefile
+--- vdr-1.7.5/Makefile 2008-12-24 16:21:09.000000000 +0100
++++ vdr-1.7.5-extensions/Makefile 2009-04-12 13:37:59.000000000 +0200
+@@ -43,6 +43,14 @@ 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 SETUP
++OBJS += tinystr.o tinyxml.o tinyxmlerror.o tinyxmlparser.o submenu.o
++endif
++
+ ifndef NO_KBD
+ DEFINES += -DREMOTE_KBD
+ endif
+@@ -72,6 +80,14 @@ DEFINES += -DLOCDIR=\"$(LOCDIR)\"
+ VDRVERSION = $(shell sed -ne '/define VDRVERSION/s/^.*"\(.*\)".*$$/\1/p' config.h)
+ APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' config.h)
+
++ifdef DVDARCHIVE
++ifdef DVDCHAPJUMP
++LIBS += -ldvdread
++INCLUDES += -I/usr/include/dvdread
++DEFINES += -DUSE_DVDCHAPJUMP
++endif
++endif
++
+ all: vdr i18n
+
+ # Implicit rules:
+@@ -137,6 +153,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="";\
+@@ -151,6 +187,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.7.5/MANUAL vdr-1.7.5-extensions/MANUAL
+--- vdr-1.7.5/MANUAL 2008-02-24 11:09:17.000000000 +0100
++++ vdr-1.7.5-extensions/MANUAL 2009-04-12 13:37:59.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.7.5/menu.c vdr-1.7.5-extensions/menu.c
+--- vdr-1.7.5/menu.c 2009-01-24 16:05:43.000000000 +0100
++++ vdr-1.7.5-extensions/menu.c 2009-04-12 13:37:59.000000000 +0200
+@@ -8,12 +8,18 @@
+ */
+
+ #include "menu.h"
++#ifdef USE_WAREAGLEICON
++#include "iconpatch.h"
++#endif /* WAREAGLEICON */
+ #include <ctype.h>
+ #include <limits.h>
+ #include <math.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"
+@@ -30,6 +36,13 @@
+ #include "timers.h"
+ #include "transfer.h"
+ #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
+@@ -190,10 +203,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)
+@@ -222,6 +241,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));
+@@ -254,6 +276,9 @@ void cMenuEditChannel::Setup(void)
+ ST(" T") Add(new cMenuEditMapItem( tr("Guard"), &data.guard, GuardValues));
+ ST(" T") Add(new cMenuEditMapItem( tr("Hierarchy"), &data.hierarchy, HierarchyValues));
+ ST(" S ") Add(new cMenuEditMapItem( tr("Rolloff"), &data.rollOff, RollOffValues));
++#ifdef USE_PLUGINPARAM
++ ST("P ") Add(new cMenuEditStrItem( tr("Parameters"), pluginParam, sizeof(pluginParam), tr(FileNameChars)));
++#endif /* PLUGINPARAM */
+
+ SetCurrent(Get(current));
+ Display();
+@@ -268,9 +293,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 {
+@@ -279,6 +310,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);
+@@ -341,6 +375,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());
+ }
+@@ -371,6 +415,9 @@ public:
+ cMenuChannels(void);
+ ~cMenuChannels();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuChannels"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuChannels::cMenuChannels(void)
+@@ -500,6 +547,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());
+@@ -525,6 +575,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());
+@@ -637,14 +690,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();
+@@ -684,6 +770,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) {
+@@ -711,13 +803,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; }
+@@ -726,6 +842,9 @@ public:
+ cMenuTimerItem::cMenuTimerItem(cTimer *Timer)
+ {
+ timer = Timer;
++#ifdef USE_TIMERINFO
++ diskStatus = ' ';
++#endif /* TIMERINFO */
+ Set();
+ }
+
+@@ -751,8 +870,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 ? " " : "",
+@@ -762,8 +929,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 {
+@@ -776,14 +999,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)) {
+@@ -794,6 +1034,9 @@ cMenuTimers::cMenuTimers(void)
+ SetCurrent(First());
+ SetHelpKeys();
+ Timers.IncBeingEdited();
++#ifdef USE_TIMERINFO
++ actualiseDiskStatus = true;
++#endif /* TIMERINFO */
+ }
+
+ cMenuTimers::~cMenuTimers()
+@@ -832,7 +1075,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
+@@ -891,6 +1138,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;
+@@ -899,18 +1257,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)
+@@ -940,6 +1320,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());
+ }
+@@ -987,7 +1370,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; }
+@@ -997,12 +1385,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);
+ }
+
+@@ -1017,7 +1412,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)
+ {
+@@ -1026,17 +1443,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;
+ }
+@@ -1062,13 +1516,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;
+@@ -1080,7 +1542,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 */
+ }
+ }
+ }
+@@ -1089,6 +1555,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;
+@@ -1229,6 +1708,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)
+@@ -1254,6 +1737,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();
+@@ -1487,6 +1983,7 @@ eOSState cMenuSchedule::ProcessKey(eKeys
+
+ // --- cMenuCommands ---------------------------------------------------------
+
++#ifndef USE_TIMERCMD
+ class cMenuCommands : public cOsdMenu {
+ private:
+ cCommands *commands;
+@@ -1496,7 +1993,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)
+@@ -1518,6 +2019,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) {
+@@ -1568,6 +2075,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)
+@@ -1747,6 +2257,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)
+@@ -1762,6 +2275,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());
+ }
+@@ -1822,7 +2338,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'
+ }
+@@ -1838,13 +2358,198 @@ 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:
++ 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;
++
++ 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 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 = int(index->Last() / recording->FramesPerSecond());
++ 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(cString::sprintf("%s:\t%s", tr("Format"), recording->IsPesRecording() ? tr("PES") : tr("TS")), osUnknown, false));
++ 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(), recording->IsPesRecording());
++ 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)) {
++ 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(), recording->IsPesRecording());
++ 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;
+@@ -1921,7 +2626,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);
+@@ -1971,13 +2681,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 */
+ }
+ }
+ }
+@@ -2078,12 +2818,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();
+@@ -2092,7 +2854,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;
+@@ -2141,6 +2922,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;
+@@ -2156,6 +2940,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)
+@@ -2191,12 +2978,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));
+@@ -2218,6 +3014,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();
+ }
+@@ -2266,6 +3080,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)
+@@ -2288,6 +3108,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;
+ }
+
+@@ -2295,12 +3134,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)
+@@ -2317,11 +3162,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));
+@@ -2330,6 +3183,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();
+@@ -2386,6 +3248,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;
+@@ -2396,6 +3262,9 @@ private:
+ public:
+ cMenuSetupDVB(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupDvb"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupDVB::cMenuSetupDVB(void)
+@@ -2424,6 +3293,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()));
+@@ -2444,6 +3328,11 @@ 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 */
+
+ SetCurrent(Get(current));
+ Display();
+@@ -2525,6 +3414,9 @@ private:
+ public:
+ cMenuSetupLNB(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupLnb"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupLNB::cMenuSetupLNB(void)
+@@ -2539,6 +3431,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));
+@@ -2555,6 +3464,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;
+@@ -2605,6 +3518,9 @@ private:
+ public:
+ cMenuSetupCAM(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupCam"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupCAM::cMenuSetupCAM(void)
+@@ -2678,12 +3594,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));
+@@ -2692,14 +3669,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, MAXVIDEOFILESIZETS));
++#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 ------------------------------------------------------
+@@ -2717,6 +3741,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)
+@@ -2729,13 +3778,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));
+@@ -2743,7 +3827,21 @@ 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 */
+ }
+
+ // --- cMenuSetupPluginItem --------------------------------------------------
+@@ -2768,6 +3866,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)
+@@ -2817,6 +3918,9 @@ private:
+ public:
+ cMenuSetup(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetup"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetup::cMenuSetup(void)
+@@ -2906,24 +4010,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)
+@@ -2933,38 +4104,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();
+@@ -2973,13 +4261,40 @@ void cMenuMain::Set(void)
+ 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) {
+@@ -2987,6 +4302,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());
+@@ -3001,6 +4319,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;
+ }
+@@ -3021,6 +4342,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;
+@@ -3028,6 +4352,12 @@ bool cMenuMain::Update(bool Force)
+ result = true;
+ }
+
++#ifdef USE_SETUP
++ // adjust nrDynamicMenuEntries
++ if (fMenu != NULL)
++ nrDynamicMenuEntries = fMenu->Index();
++#endif /* SETUP */
++
+ return result;
+ }
+
+@@ -3038,13 +4368,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) {
+@@ -3063,6 +4449,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())
+@@ -3074,9 +4463,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)
+@@ -3093,9 +4533,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) {
+@@ -3241,7 +4739,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;
+ }
+ }
+@@ -3299,6 +4804,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:
+@@ -3458,6 +4970,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:
+@@ -3707,6 +5230,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;
+@@ -3751,6 +5278,16 @@ 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)) {
+@@ -3813,11 +5350,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 */
+ }
+ }
+
+@@ -3864,6 +5415,19 @@ 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();
+@@ -3873,6 +5437,9 @@ bool cRecordControls::Start(cTimer *Time
+ 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));
+ }
+ }
+@@ -4003,12 +5570,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;
+@@ -4016,23 +5593,96 @@ 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;
+ 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);
++#ifndef USE_JUMPPLAY
+ marks.Load(fileName, Recording.FramesPerSecond(), Recording.IsPesRecording());
++#endif /* JUMPPLAY */
+ SetTrackDescriptions(false);
+ }
+
+ 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);
+@@ -4127,6 +5777,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) {
+@@ -4248,8 +5901,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();
+ }
+@@ -4262,8 +5922,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 */
+ }
+ }
+ }
+@@ -4292,11 +5966,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 * FramesPerSecond())) ? atoi(old2) : atoi(old1);
++ old2 = old1;
++ old1 = strdup(pos);
++ if(position == -1) pos = strtok(NULL, ",");
++ }
++ if (!pos && !Forward)
++ position = ((Current - atoi(old1)) <= (3 * FramesPerSecond())) ? 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))
+@@ -4318,7 +6024,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, FramesPerSecond()));
+@@ -4340,6 +6050,9 @@ eOSState cReplayControl::ProcessKey(eKey
+ {
+ if (!Active())
+ return osEnd;
++#ifdef USE_JUMPPLAY
++ marks.Reload();
++#endif /* JUMPPLAY */
+ if (visible) {
+ if (timeoutShow && time(NULL) > timeoutShow) {
+ Hide();
+@@ -4357,7 +6070,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:
+@@ -4375,25 +6113,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:
+@@ -4413,7 +6208,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.7.5/menu.h vdr-1.7.5-extensions/menu.h
+--- vdr-1.7.5/menu.h 2008-02-10 17:01:53.000000000 +0100
++++ vdr-1.7.5-extensions/menu.h 2009-04-12 13:37:59.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.7.5/menuitems.c vdr-1.7.5-extensions/menuitems.c
+--- vdr-1.7.5/menuitems.c 2009-04-05 12:15:12.000000000 +0200
++++ vdr-1.7.5-extensions/menuitems.c 2009-04-12 13:37:59.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.7.5/menuitems.h vdr-1.7.5-extensions/menuitems.h
+--- vdr-1.7.5/menuitems.h 2008-04-12 14:03:59.000000000 +0200
++++ vdr-1.7.5-extensions/menuitems.h 2009-04-12 13:37:59.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;
+@@ -185,6 +224,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.7.5/menuorgpatch.h vdr-1.7.5-extensions/menuorgpatch.h
+--- vdr-1.7.5/menuorgpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/menuorgpatch.h 2009-04-12 13:37:59.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.7.5/osdbase.c vdr-1.7.5-extensions/osdbase.c
+--- vdr-1.7.5/osdbase.c 2008-02-17 12:33:04.000000000 +0100
++++ vdr-1.7.5-extensions/osdbase.c 2009-04-12 13:37:59.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.7.5/osdbase.h vdr-1.7.5-extensions/osdbase.h
+--- vdr-1.7.5/osdbase.h 2007-11-03 15:50:52.000000000 +0100
++++ vdr-1.7.5-extensions/osdbase.h 2009-04-12 13:37:59.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,
+@@ -51,16 +55,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 +109,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 +147,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.7.5/osd.c vdr-1.7.5-extensions/osd.c
+--- vdr-1.7.5/osd.c 2009-04-05 12:17:25.000000000 +0200
++++ vdr-1.7.5-extensions/osd.c 2009-04-12 13:37:59.000000000 +0200
+@@ -724,6 +724,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)
+ {
+@@ -734,6 +737,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.7.5/osd.h vdr-1.7.5-extensions/osd.h
+--- vdr-1.7.5/osd.h 2009-04-05 12:16:05.000000000 +0200
++++ vdr-1.7.5-extensions/osd.h 2009-04-12 13:37:59.000000000 +0200
+@@ -401,6 +401,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.7.5/plugin.c vdr-1.7.5-extensions/plugin.c
+--- vdr-1.7.5/plugin.c 2009-04-05 12:16:48.000000000 +0200
++++ vdr-1.7.5-extensions/plugin.c 2009-04-12 13:37:59.000000000 +0200
+@@ -317,6 +317,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);
+ }
+@@ -325,7 +333,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.7.5/po/ca_ES.po vdr-1.7.5-extensions/po/ca_ES.po
+--- vdr-1.7.5/po/ca_ES.po 2009-04-12 11:08:04.000000000 +0200
++++ vdr-1.7.5-extensions/po/ca_ES.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1010,3 +1010,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.7.5/po/cs_CZ.po vdr-1.7.5-extensions/po/cs_CZ.po
+--- vdr-1.7.5/po/cs_CZ.po 2009-04-12 11:08:04.000000000 +0200
++++ vdr-1.7.5-extensions/po/cs_CZ.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1008,3 +1008,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.7.5/po/da_DK.po vdr-1.7.5-extensions/po/da_DK.po
+--- vdr-1.7.5/po/da_DK.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/da_DK.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1007,3 +1007,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.7.5/po/de_DE.po vdr-1.7.5-extensions/po/de_DE.po
+--- vdr-1.7.5/po/de_DE.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/de_DE.po 2009-04-12 13:37:59.000000000 +0200
+@@ -794,6 +794,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"
+
+@@ -1007,3 +1031,336 @@ 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 "Format"
++msgstr "Format"
++
++msgid "PES"
++msgstr "PES"
++
++msgid "TS"
++msgstr "TS"
++
++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.7.5/po/el_GR.po vdr-1.7.5-extensions/po/el_GR.po
+--- vdr-1.7.5/po/el_GR.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/el_GR.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1007,3 +1007,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.7.5/po/es_ES.po vdr-1.7.5-extensions/po/es_ES.po
+--- vdr-1.7.5/po/es_ES.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/es_ES.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1008,3 +1008,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.7.5/po/et_EE.po vdr-1.7.5-extensions/po/et_EE.po
+--- vdr-1.7.5/po/et_EE.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/et_EE.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1007,3 +1007,279 @@ 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 "Format"
++msgstr ""
++
++msgid "PES"
++msgstr ""
++
++msgid "TS"
++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.7.5/po/fi_FI.po vdr-1.7.5-extensions/po/fi_FI.po
+--- vdr-1.7.5/po/fi_FI.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/fi_FI.po 2009-04-12 13:37:59.000000000 +0200
+@@ -42,6 +42,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ä"
+
+@@ -1010,3 +1257,282 @@ 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 "Format"
++msgstr "Tiedostomuoto"
++
++msgid "PES"
++msgstr "PES"
++
++msgid "TS"
++msgstr "TS"
++
++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.7.5/po/fr_FR.po vdr-1.7.5-extensions/po/fr_FR.po
+--- vdr-1.7.5/po/fr_FR.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/fr_FR.po 2009-04-12 13:37:59.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 ""
+@@ -1013,3 +1014,279 @@ 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 "Format"
++msgstr "Format"
++
++msgid "PES"
++msgstr "PES"
++
++msgid "TS"
++msgstr "TS"
++
++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 "Montrer 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.7.5/po/hr_HR.po vdr-1.7.5-extensions/po/hr_HR.po
+--- vdr-1.7.5/po/hr_HR.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/hr_HR.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1009,3 +1009,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.7.5/po/hu_HU.po vdr-1.7.5-extensions/po/hu_HU.po
+--- vdr-1.7.5/po/hu_HU.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/hu_HU.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1010,3 +1010,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.7.5/po/it_IT.po vdr-1.7.5-extensions/po/it_IT.po
+--- vdr-1.7.5/po/it_IT.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/it_IT.po 2009-04-12 13:37:59.000000000 +0200
+@@ -37,6 +37,9 @@ msgstr "nessuno"
+ 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!"
+
+@@ -46,6 +49,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"
+
+@@ -232,6 +482,9 @@ msgstr "Utente8"
+ msgid "Key$User9"
+ msgstr "Utente9"
+
++msgid "Recording started"
++msgstr "Registrazione avviata"
++
+ msgid "Disk"
+ msgstr "Disco"
+
+@@ -325,6 +578,9 @@ msgstr "Gerarchia"
+ msgid "Rolloff"
+ msgstr "Rolloff"
+
++msgid "Parameters"
++msgstr "Parametri"
++
+ msgid "Channel settings are not unique!"
+ msgstr "Parametri canale non univoci!"
+
+@@ -376,9 +632,15 @@ msgstr "Priorità"
+ msgid "Lifetime"
+ msgstr "Scadenza"
+
++msgid "Childlock"
++msgstr "Filtro fam."
++
+ msgid "File"
+ msgstr "Nome"
+
++msgid "Path"
++msgstr "Percorso"
++
+ msgid "First day"
+ msgstr "1° giorno"
+
+@@ -397,6 +659,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"
+
+@@ -457,6 +722,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"
+
+@@ -466,8 +752,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?"
+@@ -478,6 +764,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"
+
+@@ -487,6 +788,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"
+
+@@ -499,6 +812,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"
+
+@@ -568,12 +884,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)"
+
+@@ -583,6 +917,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"
+
+@@ -597,6 +934,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"
+
+@@ -627,6 +985,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"
+
+@@ -666,9 +1045,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"
+
+@@ -711,6 +1109,9 @@ msgstr "La CAM è in uso - vuoi reimpost
+ msgid "Can't reset CAM!"
+ msgstr "Impossibile reimpostare il modulo CAM!"
+
++msgid "request"
++msgstr "chiedi"
++
+ msgid "Recording"
+ msgstr "Registrazione"
+
+@@ -735,9 +1136,29 @@ msgstr "Priorità di pausa"
+ msgid "Setup.Recording$Pause lifetime (d)"
+ msgstr "Scadenza 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"
+
+@@ -756,9 +1177,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"
+
+@@ -771,6 +1222,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"
+
+@@ -798,9 +1306,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"
+
+@@ -826,7 +1379,6 @@ msgstr "Programmi"
+ msgid "VDR"
+ msgstr "VDR"
+
+-#. TRANSLATORS: note the leading blank!
+ msgid " Stop replaying"
+ msgstr " Ferma riproduzione"
+
+@@ -842,7 +1394,6 @@ msgstr "Ferma"
+ msgid "Button$Resume"
+ msgstr "Riprendi"
+
+-#. TRANSLATORS: note the leading blank!
+ msgid " Cancel editing"
+ msgstr " Annulla modifiche"
+
+@@ -873,10 +1424,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!"
+
+@@ -907,6 +1470,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"
+
+@@ -919,6 +1494,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!"
+
+@@ -990,9 +1589,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"
+
+diff -ruNp vdr-1.7.5/po/nl_NL.po vdr-1.7.5-extensions/po/nl_NL.po
+--- vdr-1.7.5/po/nl_NL.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/nl_NL.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1011,3 +1011,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.7.5/po/nn_NO.po vdr-1.7.5-extensions/po/nn_NO.po
+--- vdr-1.7.5/po/nn_NO.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/nn_NO.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1008,3 +1008,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.7.5/po/pl_PL.po vdr-1.7.5-extensions/po/pl_PL.po
+--- vdr-1.7.5/po/pl_PL.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/pl_PL.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1008,3 +1008,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.7.5/po/pt_PT.po vdr-1.7.5-extensions/po/pt_PT.po
+--- vdr-1.7.5/po/pt_PT.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/pt_PT.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1007,3 +1007,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.7.5/po/ro_RO.po vdr-1.7.5-extensions/po/ro_RO.po
+--- vdr-1.7.5/po/ro_RO.po 2009-04-12 11:08:05.000000000 +0200
++++ vdr-1.7.5-extensions/po/ro_RO.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1010,3 +1010,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.7.5/po/ru_RU.po vdr-1.7.5-extensions/po/ru_RU.po
+--- vdr-1.7.5/po/ru_RU.po 2009-04-12 11:08:06.000000000 +0200
++++ vdr-1.7.5-extensions/po/ru_RU.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1008,3 +1008,280 @@ 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 "Format"
++msgstr ""
++
++msgid "PES"
++msgstr ""
++
++msgid "TS"
++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.7.5/po/sl_SI.po vdr-1.7.5-extensions/po/sl_SI.po
+--- vdr-1.7.5/po/sl_SI.po 2009-04-12 11:08:06.000000000 +0200
++++ vdr-1.7.5-extensions/po/sl_SI.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1008,3 +1008,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.7.5/po/sv_SE.po vdr-1.7.5-extensions/po/sv_SE.po
+--- vdr-1.7.5/po/sv_SE.po 2009-04-12 11:08:06.000000000 +0200
++++ vdr-1.7.5-extensions/po/sv_SE.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1010,3 +1010,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.7.5/po/tr_TR.po vdr-1.7.5-extensions/po/tr_TR.po
+--- vdr-1.7.5/po/tr_TR.po 2009-04-12 11:08:06.000000000 +0200
++++ vdr-1.7.5-extensions/po/tr_TR.po 2009-04-12 13:37:59.000000000 +0200
+@@ -1007,3 +1007,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.7.5/README.cmdsubmenu vdr-1.7.5-extensions/README.cmdsubmenu
+--- vdr-1.7.5/README.cmdsubmenu 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/README.cmdsubmenu 2009-04-12 13:37:59.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.7.5/README-HLCUTTER vdr-1.7.5-extensions/README-HLCUTTER
+--- vdr-1.7.5/README-HLCUTTER 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/README-HLCUTTER 2009-04-12 13:37:59.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.7.5/README.jumpplay vdr-1.7.5-extensions/README.jumpplay
+--- vdr-1.7.5/README.jumpplay 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/README.jumpplay 2009-04-12 13:37:59.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.7.5/README.MainMenuHooks vdr-1.7.5-extensions/README.MainMenuHooks
+--- vdr-1.7.5/README.MainMenuHooks 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/README.MainMenuHooks 2009-04-12 13:37:59.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.7.5/README.sortrec vdr-1.7.5-extensions/README.sortrec
+--- vdr-1.7.5/README.sortrec 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/README.sortrec 2009-04-12 13:37:59.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.7.5/README.timer-info vdr-1.7.5-extensions/README.timer-info
+--- vdr-1.7.5/README.timer-info 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/README.timer-info 2009-04-12 13:37:59.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.7.5/recorder.c vdr-1.7.5-extensions/recorder.c
+--- vdr-1.7.5/recorder.c 2009-03-20 16:49:02.000000000 +0100
++++ vdr-1.7.5-extensions/recorder.c 2009-04-12 13:37:59.000000000 +0200
+@@ -22,7 +22,11 @@
+ // --- cRecorder -------------------------------------------------------------
+
+ cRecorder::cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
++#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")
+ ,recordingInfo(FileName)
+ {
+@@ -85,7 +89,11 @@ bool cRecorder::RunningLowOnDiskSpace(vo
+ bool cRecorder::NextFile(void)
+ {
+ if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame
++#ifndef USE_HARDLINKCUTTER
+ if (fileSize > MEGABYTE(off_t(Setup.MaxVideoFileSize)) || RunningLowOnDiskSpace()) {
++#else
++ if (fileSize > fileName->MaxFileSize() || RunningLowOnDiskSpace()) {
++#endif /* HARDLINKCUTTER */
+ recordFile = fileName->NextFile();
+ fileSize = 0;
+ }
+diff -ruNp vdr-1.7.5/recording.c vdr-1.7.5-extensions/recording.c
+--- vdr-1.7.5/recording.c 2009-01-30 17:27:19.000000000 +0100
++++ vdr-1.7.5-extensions/recording.c 2009-04-12 13:37:59.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
+
+@@ -48,6 +62,12 @@
+ #endif
+ #define INFOFILESUFFIX "/info"
+ #define MARKSFILESUFFIX "/marks"
++#ifdef USE_LIEMIEXT
++#define INDEXFILESUFFIX "/index"
++#endif /* LIEMIEXT */
++#ifdef USE_DVDARCHIVE /* ??? */
++#define DVDARCHIVEFILENAME "/dvd.vdr"
++#endif /* DVDARCHIVE */
+
+ #define MINDISKSPACE 1024 // MB
+
+@@ -65,6 +85,13 @@
+
+ bool VfatFileSystem = false;
+ int InstanceId = 0;
++#ifdef USE_LIEMIEXT
++bool DirOrderState = false;
++#endif /* LIEMIEXT */
++
++#ifdef USE_DVLFRIENDLYFNAMES
++char *MakeFriendlyFilename(char **buf);
++#endif /* DVLFRIENDLYFNAMES */
+
+ cRecordings DeletedRecordings(true);
+
+@@ -599,9 +626,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
+ channel = Timer->Channel()->Number();
+ instanceId = InstanceId;
+@@ -636,6 +678,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();
+ }
+@@ -646,6 +693,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();
+@@ -668,12 +719,27 @@ cRecording::cRecording(const char *FileN
+ framesPerSecond = DEFAULTFRAMESPERSECOND;
+ 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);
+@@ -762,15 +828,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;
+ }
+
+@@ -790,21 +875,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
+@@ -819,7 +929,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
+@@ -837,9 +955,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()) {
+@@ -850,7 +1318,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,
+@@ -860,6 +1335,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'", (long int)buf.st_size, *filename);
++ }
++ RecLength = cString::sprintf("%ld'", (long int)(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);
+@@ -888,6 +1449,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);
+@@ -941,10 +1513,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
+@@ -962,7 +1544,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)
+@@ -979,8 +1567,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;
+@@ -996,6 +1591,54 @@ void cRecording::ResetResume(void) const
+ resume = RESUME_NOT_INITIALIZED;
+ }
+
++#ifdef USE_LIEMIEXT
++bool cRecording::Rename(const char *newName)
++{
++ bool result = false;
++ struct tm tm_r;
++ struct tm *t = localtime_r(&start, &tm_r);
++ char *localNewName = ExchangeChars(strdup(newName), true);
++ const char *fmt = isPesRecording ? NAMEFORMATPES : NAMEFORMATTS;
++ int ch = isPesRecording ? priority : channel;
++ int ri = isPesRecording ? lifetime : instanceId;
++ char *newFileName = strdup(cString::sprintf(fmt, VideoDirectory, localNewName, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, ch, ri));
++ 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) {
++ 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;
+@@ -1008,6 +1651,9 @@ cRecordings::cRecordings(bool Deleted)
+ deleted = Deleted;
+ lastUpdate = 0;
+ state = 0;
++#ifdef USE_SORTRECORDS
++ SortOrder = 1;
++#endif /* SORTRECORDS */
+ }
+
+ cRecordings::~cRecordings()
+@@ -1294,14 +1940,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);
+ }
+@@ -1309,7 +2011,9 @@ void cRecordingUserCommand::InvokeComman
+
+ // --- cIndexFile ------------------------------------------------------------
+
++#ifndef USE_LIEMIEXT
+ #define INDEXFILESUFFIX "/index"
++#endif /* LIEMIEXT */
+
+ // The number of frames to stay off the end in case of time shift:
+ #define INDEXSAFETYLIMIT 150 // frames
+@@ -1625,6 +2329,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);
+ }
+
+@@ -1703,6 +2449,22 @@ cUnbufferedFile *cFileName::SetOffset(in
+ return NULL;
+ }
+
++#ifdef USE_HARDLINKCUTTER
++off_t cFileName::MaxFileSize() {
++ const int maxVideoFileSize = isPesRecording ? MAXVIDEOFILESIZEPES : MAXVIDEOFILESIZETS;
++ const int setupMaxVideoFileSize = min(maxVideoFileSize, Setup.MaxVideoFileSize);
++ const int maxFileNumber = isPesRecording ? 255 : 65535;
++
++ const off_t smallFiles = (maxFileNumber * off_t(maxVideoFileSize) - 1024 * Setup.MaxRecordingSize)
++ / max(maxVideoFileSize - setupMaxVideoFileSize, 1);
++
++ if (fileNumber <= smallFiles)
++ return MEGABYTE(off_t(setupMaxVideoFileSize));
++
++ return MEGABYTE(off_t(maxVideoFileSize));
++}
++#endif /* HARDLINKCUTTER */
++
+ cUnbufferedFile *cFileName::NextFile(void)
+ {
+ return SetOffset(fileNumber + 1);
+@@ -1754,3 +2516,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.7.5/recording.h vdr-1.7.5-extensions/recording.h
+--- vdr-1.7.5/recording.h 2009-02-28 11:50:12.000000000 +0100
++++ vdr-1.7.5-extensions/recording.h 2009-04-12 13:37:59.000000000 +0200
+@@ -20,6 +20,9 @@
+
+ extern bool VfatFileSystem;
+ extern int InstanceId;
++#ifdef USE_LIEMIEXT
++extern bool DirOrderState;
++#endif /* LIEMIEXT */
+
+ void RemoveDeletedRecordings(void);
+ void AssertFreeDiskSpace(int Priority = 0, bool Force = false);
+@@ -63,6 +66,9 @@ public:
+ const cEvent *GetEvent(void) const { return event; }
+ 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; }
+@@ -74,12 +80,36 @@ public:
+ bool Write(void) 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;
+@@ -88,6 +118,15 @@ private:
+ bool isPesRecording;
+ double framesPerSecond;
+ 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);
+@@ -104,8 +143,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;
+@@ -124,6 +178,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);
++ // Changes the file name
++ // Returns false in case of error
++#endif /* LIEMIEXT */
+ };
+
+ class cRecordings : public cList<cRecording>, public cThread {
+@@ -132,6 +191,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);
+@@ -162,6 +224,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;
+@@ -194,6 +260,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"
+@@ -202,8 +282,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 */
+ };
+
+ // The maximum size of a single frame (up to HDTV 1920x1080):
+@@ -216,9 +303,23 @@ public:
+ // before the next independent frame, to have a complete Group Of Pictures):
+ #define MAXVIDEOFILESIZETS 1048570 // MB
+ #define MAXVIDEOFILESIZEPES 2000 // MB
++#ifdef USE_HARDLINKCUTTER
++#define MINVIDEOFILESIZE 1 // MB
++#else
+ #define MINVIDEOFILESIZE 100 // MB
++#endif /* HARDLINKCUTTER */
+ #define MAXVIDEOFILESIZEDEFAULT MAXVIDEOFILESIZEPES
+
++#ifdef USE_HARDLINKCUTTER
++#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 */
++
+ struct tIndexTs;
+
+ class cIndexFile {
+@@ -233,6 +334,9 @@ private:
+ void ConvertFromPes(tIndexTs *IndexTs, int Count);
+ void ConvertToPes(tIndexTs *IndexTs, int Count);
+ bool CatchUp(int Index = -1);
++#ifdef USE_DVDARCHIVE
++ bool isOnDVD;
++#endif /* DVDARCHIVE */
+ public:
+ cIndexFile(const char *FileName, bool Record, bool IsPesRecording = false);
+ ~cIndexFile();
+@@ -263,6 +367,9 @@ public:
+ cUnbufferedFile *Open(void);
+ void Close(void);
+ cUnbufferedFile *SetOffset(int Number, off_t Offset = 0);
++#ifdef USE_HARDLINKCUTTER
++ off_t MaxFileSize(); // Dynamic file size for this file
++#endif /* HARDLINKCUTTER */
+ cUnbufferedFile *NextFile(void);
+ };
+
+diff -ruNp vdr-1.7.5/skinclassic.c vdr-1.7.5-extensions/skinclassic.c
+--- vdr-1.7.5/skinclassic.c 2008-02-23 11:31:58.000000000 +0100
++++ vdr-1.7.5-extensions/skinclassic.c 2009-04-12 13:37:59.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.7.5/skinsttng.c vdr-1.7.5-extensions/skinsttng.c
+--- vdr-1.7.5/skinsttng.c 2008-02-23 11:23:44.000000000 +0100
++++ vdr-1.7.5-extensions/skinsttng.c 2009-04-12 13:37:59.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.7.5/sources.c vdr-1.7.5-extensions/sources.c
+--- vdr-1.7.5/sources.c 2008-02-10 15:07:26.000000000 +0100
++++ vdr-1.7.5-extensions/sources.c 2009-04-12 13:37:59.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.7.5/sources.conf vdr-1.7.5-extensions/sources.conf
+--- vdr-1.7.5/sources.conf 2008-08-16 12:02:27.000000000 +0200
++++ vdr-1.7.5-extensions/sources.conf 2009-04-12 13:37:59.000000000 +0200
+@@ -194,3 +194,7 @@ C Cable
+ # Terrestrial
+
+ T Terrestrial
++
++# Plugin PLUGINPARAM
++
++#P Plugin
+diff -ruNp vdr-1.7.5/sources.h vdr-1.7.5-extensions/sources.h
+--- vdr-1.7.5/sources.h 2005-05-14 11:30:41.000000000 +0200
++++ vdr-1.7.5-extensions/sources.h 2009-04-12 13:37:59.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.7.5/status.c vdr-1.7.5-extensions/status.c
+--- vdr-1.7.5/status.c 2008-02-16 15:46:31.000000000 +0100
++++ vdr-1.7.5-extensions/status.c 2009-04-12 13:37:59.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,88 @@ 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.7.5/status.h vdr-1.7.5-extensions/status.h
+--- vdr-1.7.5/status.h 2008-02-16 16:00:33.000000000 +0100
++++ vdr-1.7.5-extensions/status.h 2009-04-12 13:37:59.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,46 @@ 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 +152,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.7.5/submenu.c vdr-1.7.5-extensions/submenu.c
+--- vdr-1.7.5/submenu.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/submenu.c 2009-04-12 13:37:59.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.7.5/submenu.h vdr-1.7.5-extensions/submenu.h
+--- vdr-1.7.5/submenu.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/submenu.h 2009-04-12 13:37:59.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.7.5/svdrp.c vdr-1.7.5-extensions/svdrp.c
+--- vdr-1.7.5/svdrp.c 2009-01-06 15:35:45.000000000 +0100
++++ vdr-1.7.5-extensions/svdrp.c 2009-04-12 13:37:59.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
+@@ -1466,6 +1485,38 @@ 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) {
++ char *oldName = strdup(recording->Name());
++ tail = skipspace(tail);
++ if (recording->Rename(tail)) {
++ 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) {
+@@ -1581,6 +1632,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.7.5/svdrp.h vdr-1.7.5-extensions/svdrp.h
+--- vdr-1.7.5/svdrp.h 2007-04-30 14:28:28.000000000 +0200
++++ vdr-1.7.5-extensions/svdrp.h 2009-04-12 13:37:59.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.7.5/timers.c vdr-1.7.5-extensions/timers.c
+--- vdr-1.7.5/timers.c 2008-04-13 14:41:41.000000000 +0200
++++ vdr-1.7.5-extensions/timers.c 2009-04-12 13:37:59.000000000 +0200
+@@ -46,6 +46,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 +82,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 +128,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 +325,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 +574,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 +640,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;
+diff -ruNp vdr-1.7.5/timers.h vdr-1.7.5-extensions/timers.h
+--- vdr-1.7.5/timers.h 2008-02-16 15:33:23.000000000 +0100
++++ vdr-1.7.5-extensions/timers.h 2009-04-12 13:37:59.000000000 +0200
+@@ -37,6 +37,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 +61,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 +92,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.7.5/tinystr.c vdr-1.7.5-extensions/tinystr.c
+--- vdr-1.7.5/tinystr.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/tinystr.c 2009-04-12 13:37:59.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.7.5/tinystr.h vdr-1.7.5-extensions/tinystr.h
+--- vdr-1.7.5/tinystr.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/tinystr.h 2009-04-12 13:37:59.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.7.5/tinyxml.c vdr-1.7.5-extensions/tinyxml.c
+--- vdr-1.7.5/tinyxml.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/tinyxml.c 2009-04-12 13:37:59.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.7.5/tinyxmlerror.c vdr-1.7.5-extensions/tinyxmlerror.c
+--- vdr-1.7.5/tinyxmlerror.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/tinyxmlerror.c 2009-04-12 13:37:59.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.7.5/tinyxml.h vdr-1.7.5-extensions/tinyxml.h
+--- vdr-1.7.5/tinyxml.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/tinyxml.h 2009-04-12 13:37:59.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.7.5/tinyxmlparser.c vdr-1.7.5-extensions/tinyxmlparser.c
+--- vdr-1.7.5/tinyxmlparser.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-extensions/tinyxmlparser.c 2009-04-12 13:37:59.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.7.5/vdr.c vdr-1.7.5-extensions/vdr.c
+--- vdr-1.7.5/vdr.c 2009-04-05 15:21:46.000000000 +0200
++++ vdr-1.7.5-extensions/vdr.c 2009-04-12 13:37:59.000000000 +0200
+@@ -139,10 +139,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 +170,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 +198,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;
+@@ -221,6 +235,7 @@ int main(int argc, char *argv[])
+ { "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' },
+ { "instance", required_argument, NULL, 'i' },
+@@ -236,6 +251,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' },
+@@ -246,7 +264,11 @@ int main(int argc, char *argv[])
+ };
+
+ int c;
++#ifdef USE_SETTIME
++ while ((c = getopt_long(argc, argv, "a:c:dD:E:g:hi:l: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:hi:l:L:mp:P:r:s:t:u:v:Vw:", long_options, NULL)) != -1) {
++#endif /* SETTIME */
+ switch (c) {
+ case 'a': AudioCommand = optarg;
+ break;
+@@ -263,6 +285,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);
+@@ -341,6 +366,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);
+@@ -406,6 +435,7 @@ int main(int argc, char *argv[])
+ " -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"
+@@ -437,6 +467,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"
+@@ -480,6 +513,197 @@ 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_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_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_TIMERCMD
++ printf(" TIMERCMD\n");
++#endif /* TIMERCMD */
++
++#ifdef USE_TIMERINFO
++ printf(" TIMERINFO\n");
++#endif /* TIMERINFO */
++
++#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)
+@@ -573,8 +797,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);
+@@ -735,7 +971,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...
+@@ -910,6 +1150,9 @@ int main(int argc, char *argv[])
+ cOsdObject *Interact = Menu ? Menu : cControl::Control();
+ eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
+ if (ISREALKEY(key)) {
++#ifdef USE_PINPLUGIN
++ cStatus::MsgUserAction(key, Interact);
++#endif /* PINPLUGIN */
+ EITScanner.Activity();
+ // Cancel shutdown countdown:
+ if (ShutdownHandler.countdown)
+@@ -982,10 +1225,16 @@ int main(int argc, char *argv[])
+ cControl::Control()->Hide();
+ 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);
+ }
+@@ -1062,8 +1311,15 @@ int main(int argc, char *argv[])
+ // Instant recording:
+ case kRecord:
+ if (!cControl::Control()) {
++#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;
+@@ -1119,8 +1375,15 @@ int main(int argc, char *argv[])
+ Skins.Message(mtError, tr("No free DVB device to record!"));
+ break;
+ case osRecord: DELETE_MENU;
++#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;
+@@ -1171,13 +1434,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:
+@@ -1195,9 +1471,15 @@ 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;
+ }
+diff -ruNp vdr-1.7.5/videodir.c vdr-1.7.5-extensions/videodir.c
+--- vdr-1.7.5/videodir.c 2008-02-16 14:00:03.000000000 +0100
++++ vdr-1.7.5-extensions/videodir.c 2009-04-12 13:37:59.000000000 +0200
+@@ -19,6 +19,13 @@
+ #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;
+
+ class cVideoDirectory {
+@@ -36,6 +43,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 +129,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 +141,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 +193,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 +376,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.7.5/videodir.h vdr-1.7.5-extensions/videodir.h
+--- vdr-1.7.5/videodir.h 2008-02-16 13:53:11.000000000 +0100
++++ vdr-1.7.5-extensions/videodir.h 2009-04-12 13:37:59.000000000 +0200
+@@ -19,6 +19,9 @@ cUnbufferedFile *OpenVideoFile(const cha
+ 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);
diff --git a/vdr/install-vdr.sh b/vdr/install-vdr.sh
new file mode 100644
index 0000000..bcbe098
--- /dev/null
+++ b/vdr/install-vdr.sh
@@ -0,0 +1,987 @@
+#!/bin/sh
+
+# x-vdr (Installations-Skript fuer einen VDR mit Debian als Basis)
+# von Marc Wernecke - www.zulu-entertainment.de
+# 10.04.2009
+
+# defaults
+VDRVERSION="1.6.0"
+MTPATCHES="" # MTPATCHES="1 2 3"
+VDROPTIONS="" # hier bitte nur zusaetzliche Optionen eintragen
+DIR=$(pwd)
+
+# config files
+[ -f ./patchlevel.conf ] && source ./patchlevel.conf
+source ./../x-vdr.conf
+source ./../setup.conf
+# functions
+source ./../functions
+
+verify_vdrversion "$VDRVERSION" || exit 1
+[ $(( ${VDRVERSION:2:1} % 2 )) -eq 0 ] && VDRWEB="ftp://ftp.cadsoft.de/vdr" || VDRWEB="ftp://ftp.cadsoft.de/vdr/Developer"
+
+# create error file
+error_file="$SOURCEDIR/x-vdr/.error"
+echo "install-vdr.sh " > $error_file
+log "****************************************"
+log "Vorbereitungen fuer den VDR ..."
+log "****************************************"
+
+# check input (on/off)
+AUTOSTART=`verify_input "$AUTOSTART"`
+INS_USER=`verify_input "$INS_USER"`
+USEVFAT=`verify_input "$USEVFAT"`
+USELIRC=`verify_input "$USELIRC"`
+VDRUPDATE=`verify_input "$VDRUPDATE"`
+vdr_plugins=`verify_input "$vdr_plugins"`
+analogtv=`verify_input "$analogtv"`
+burn=`verify_input "$burn"`
+channelblocker=`verify_input "$channelblocker"`
+dxr3=`verify_input "$dxr3"`
+epgsearch=`verify_input "$epgsearch"`
+extrecmenu=`verify_input "$extrecmenu"`
+graphtft=`verify_input "$graphtft"`
+iptv=`verify_input "$iptv"`
+menuorg=`verify_input "$menuorg"`
+noepgmenu=`verify_input "$noepgmenu"`
+pin=`verify_input "$pin"`
+pvrinput=`verify_input "$pvrinput"`
+pvrusb2=`verify_input "$pvrusb2"`
+reelbox=`verify_input "$reelbox"`
+reelchannelscan=`verify_input "$reelchannelscan"`
+remoteosd=`verify_input "$remoteosd"`
+remotetimers=`verify_input "$remotetimers"`
+rotor=`verify_input "$rotor"`
+setup=`verify_input "$setup"`
+submenu=`verify_input "$submenu"`
+subtitles=`verify_input "$subtitles"`
+ttxtsubs=`verify_input "$ttxtsubs"`
+yaepg=`verify_input "$yaepg"`
+verify_patchlevel
+
+# pre-install
+if [ ! -d $SOURCEDIR/DVB ]; then
+ if [ -d /usr/include/linux/dvb ]; then
+ if [ ! -f /usr/include/linux/compiler.h ]; then
+ cd /usr/include/linux
+ if [ -f /usr/src/linux/include/linux/compiler.h ]; then
+ ln -s /usr/src/linux/include/linux/compiler.h compiler.h
+ elif [ -f /usr/src/$(uname -r)/include/linux/compiler.h ]; then
+ ln -s /usr/src/$(uname -r)/include/linux/compiler.h compiler.h
+ elif [ -f /usr/src/linux-headers-$(uname -r)/include/linux/compiler.h ]; then
+ ln -s /usr/src/linux-headers-$(uname -r)/include/linux/compiler.h compiler.h
+ else
+ log "***************************************"
+ log "VDR Fehler! - compiler.h nicht gefunden"
+ log "***************************************"
+ echo "- /usr/src/linux/include/linux/compiler.h nicht gefunden." >> $error_file
+ exit 1
+ fi
+ fi
+ else
+ log "****************************************"
+ log "VDR Fehler! - DVB Treiber nicht gefunden"
+ log "****************************************"
+ echo "- dvb-treiber nicht gefunden." >> $error_file
+ exit 1
+ fi
+fi
+
+# DVB_API_VERSION
+get_dvbapi
+
+## VDR-Source
+# download VDR-Source
+if [ ! -f $FILES/vdr/vdr-$VDRVERSION.tar.bz2 ]; then
+ log "vdr-$VDRVERSION.tar.bz2 nicht gefunden"
+ log "Starte download"
+ wget --tries=2 "$VDRWEB/vdr-$VDRVERSION.tar.bz2" --directory-prefix="$FILES/vdr"
+else
+ log "vdr-$VDRVERSION.tar.bz2 gefunden"
+fi
+
+if [ ! -f "$FILES/vdr/vdr-$VDRVERSION.tar.bz2" ]; then
+ log "****************************************"
+ log "VDR Fehler!"
+ log "vdr-$VDRVERSION.tar.bz2 nicht gefunden"
+ log "****************************************"
+ echo "- vdr-$VDRVERSION.tar.bz2 konnte nicht geladen werden" >> $error_file
+ exit 1
+fi
+
+# untar VDR-Source
+tar xjf "$FILES/vdr/vdr-$VDRVERSION.tar.bz2" -C $SOURCEDIR && log "Extrahiere vdr-$VDRVERSION.tar.bz2"
+
+# create VDR-Symlink
+cd $SOURCEDIR
+ln -vnfs vdr-$VDRVERSION VDR
+
+# create vdruser
+[ "$VDRUPDATE" != "on" ] && make_user
+
+# create Directories
+[ "$VDRUPDATE" != "on" ] && make_dirs
+
+# create VDR make.config
+function make_makeconfig() {
+ MAKECONFIG="$SOURCEDIR/VDR/Make.config"
+ [ -f $MAKECONFIG ] && mv -f $MAKECONFIG $MAKECONFIG.old
+
+ {
+ echo "#"
+ echo "# User defined Makefile options for the Video Disk Recorder"
+ echo "#"
+ echo "# Copy this file to 'Make.config' and change the parameters as necessary."
+ echo "#"
+ echo "# See the main source file 'vdr.c' for copyright information and"
+ echo "# how to reach the author."
+ echo "#"
+ echo "# \$Id: Make.config.template 1.10 2006/06/15 09:15:25 kls Exp \$"
+ echo ""
+ echo "# $(date)"
+ echo "# modified for x-vdr"
+ echo ""
+ echo "### The C compiler and options:"
+ echo "CC = gcc"
+ echo "CFLAGS = -g -O2 -Wall"
+ echo ""
+ echo "CXX = g++"
+ echo "CXXFLAGS = -g -O2 -Wall -Woverloaded-virtual"
+ echo ""
+ echo "ifdef PLUGIN"
+ echo "CFLAGS += -fPIC"
+ echo "CXXFLAGS += -fPIC"
+ if [ ${VDRVERSION:2:1} -gt 6 -a ${VDRVERSION:4:1} -gt 2 -o ${VDRVERSION:2:1} -gt 7 ]; then
+ echo "DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE"
+ fi
+ echo "endif"
+ echo ""
+ echo "DEFINES += -D__KERNEL_STRICT_NAMES"
+ echo ""
+ echo "### The directory environment:"
+ echo ""
+ if [ -f "$SOURCEDIR/DVB/linux/include/linux/dvb/version.h" ]; then
+ echo "DVBDIR= $SOURCEDIR/DVB/linux"
+ else
+ echo "#DVBDIR= $SOURCEDIR/v4l-dvb/linux"
+ fi
+ echo "MANDIR= $VDRMANDIR"
+ echo "BINDIR= $VDRBINDIR"
+ echo ""
+ echo "LOCDIR= $VDRLOCDIR"
+ echo "PLUGINDIR= ./PLUGINS"
+ echo "PLUGINLIBDIR= $VDRLIBDIR"
+ echo "VIDEODIR= $VIDEODIR"
+ echo "CONFDIR= $VDRCONFDIR"
+ echo ""
+ echo "LIBDIR= \$(PLUGINLIBDIR)"
+ echo ""
+ echo "### The remote control:"
+ echo ""
+ echo "LIRC_DEVICE = /dev/lircd"
+ echo "RCU_DEVICE = /dev/ttyS1"
+ echo ""
+ echo "## Define if you want vdr to not run as root"
+ echo "#VDR_USER = $VDRUSER"
+ echo ""
+ echo "### Extra Options:"
+ echo ""
+
+ if [ "$USELIRC" = "on" ]; then
+ echo "REMOTE= LIRC"
+ else
+ echo "#REMOTE= LIRC"
+ fi
+
+ if [ "$USEVFAT" = "on" ]; then
+ echo "VFAT= 1"
+ else
+ echo "#VFAT= 1"
+ fi
+
+ if [ "$dxr3" = "on" ]; then
+ echo "EM8300= $SOURCEDIR/em8300/include"
+ fi
+
+ if [ "$ffmpeg" = "on" ]; then
+ echo "FFMDIR= $SOURCEDIR/ffmpeg"
+ else
+ echo "FFMDIR= /usr/include/ffmpeg"
+ fi
+
+ if [ "$burn" = "on" ]; then
+ echo "DVDDEV= $DVDBURNER"
+ echo "ISODIR= $DVDISODIR"
+ fi
+
+ if [ "$tvm2vdr" = "on" ]; then
+ echo "DEFINES += -DTVM2VDR_DATA_DIR=$VDRVARDIR"
+ else
+ echo "#DEFINES += -DTVM2VDR_DATA_DIR=$VDRVARDIR"
+ fi
+
+ if [ "$PATCHLEVEL" = "EXTENSIONS" ]; then
+ echo ""
+ echo "### VDR-Extensions:"
+ echo "# Comment the patches you don't need"
+ echo "# DVDCHAPJUMP needs DVDARCHIVE enabled"
+ echo "# DVDARCHIVE needs LIEMIEXT enabled"
+ echo "# SORTRECORDS needs LIEMIEXT enabled"
+ echo "# you can only enable one of MENUORG SUBMENU SETUP"
+ echo ""
+
+ if [ "$ANALOGTV" = "on" ] || [ "$analogtv" = "on" ] || [ "$pvrinput" = "on" ] || [ "$pvrusb2" = "on" ]; then
+ echo "ANALOGTV = 1"
+ else
+ echo "#ANALOGTV = 1"
+ fi
+
+ if [ "$ATSC" = "on" ]; then
+ echo "ATSC = 1"
+ else
+ echo "#ATSC = 1"
+ fi
+
+ if [ "$CHANNELSCAN" = "on" ] || [ "$reelchannelscan" = "on" ]; then
+ echo "CHANNELSCAN = 1"
+ else
+ echo "#CHANNELSCAN = 1"
+ fi
+
+ if [ "$CMDRECCMDI18N" = "on" ]; then
+ echo "CMDRECCMDI18N = 1"
+ else
+ echo "#CMDRECCMDI18N = 1"
+ fi
+
+ if [ "$CMDSUBMENU" = "on" ]; then
+ echo "CMDSUBMENU = 1"
+ else
+ echo "#CMDSUBMENU = 1"
+ fi
+
+ if [ "$CUTTERLIMIT" = "on" ]; then
+ echo "CUTTERLIMIT = 1"
+ else
+ echo "#CUTTERLIMIT = 1"
+ fi
+
+ if [ "$CUTTERQUEUE" = "on" ]; then
+ echo "CUTTERQUEUE = 1"
+ else
+ echo "#CUTTERQUEUE = 1"
+ fi
+
+ if [ "$CUTTIME" = "on" ]; then
+ echo "CUTTIME = 1"
+ else
+ echo "#CUTTIME = 1"
+ fi
+
+ if [ "$DELTIMESHIFTREC" = "on" ]; then
+ echo "DELTIMESHIFTREC = 1"
+ else
+ echo "#DELTIMESHIFTREC = 1"
+ fi
+
+ if [ "$DDEPGENTRY" = "on" ]; then
+ echo "DDEPGENTRY = 1"
+ else
+ echo "#DDEPGENTRY = 1"
+ fi
+
+ if [ "$DOLBYINREC" = "on" ]; then
+ echo "DOLBYINREC = 1"
+ else
+ echo "#DOLBYINREC = 1"
+ fi
+
+ if [ "$DVBPLAYER" = "on" ]; then
+ echo "DVBPLAYER = 1"
+ else
+ echo "#DVBPLAYER = 1"
+ fi
+
+ if [ "$DVBSETUP" = "on" ] || [ "$channelblocker" = "on" ]; then
+ echo "DVBSETUP = 1"
+ else
+ echo "#DVBSETUP = 1"
+ fi
+
+ if [ "$DVDARCHIVE" = "on" ]; then
+ echo "DVDARCHIVE = 1"
+ else
+ echo "#DVDARCHIVE = 1"
+ fi
+
+ if [ "$DVDCHAPJUMP" = "on" ]; then
+ echo "DVDCHAPJUMP = 1"
+ else
+ echo "#DVDCHAPJUMP = 1"
+ fi
+
+ if [ "$DVLFRIENDLYFNAMES" = "on" ]; then
+ echo "DVLFRIENDLYFNAMES = 1"
+ else
+ echo "#DVLFRIENDLYFNAMES = 1"
+ fi
+
+ if [ "$DVLRECSCRIPTADDON" = "on" ]; then
+ echo "DVLRECSCRIPTADDON = 1"
+ else
+ echo "#DVLRECSCRIPTADDON = 1"
+ fi
+
+ if [ "$DVLVIDPREFER" = "on" ]; then
+ echo "DVLVIDPREFER = 1"
+ else
+ echo "#DVLVIDPREFER = 1"
+ fi
+
+ if [ "$EM84XX" = "on" ] || [ "$em84xx" = "on" ]; then
+ echo "EM84XX = 1"
+ else
+ echo "#EM84XX = 1"
+ fi
+
+ if [ "$GOTOX" = "on" ]; then
+ echo "GOTOX = 1"
+ else
+ echo "#GOTOX = 1"
+ fi
+
+ if [ "$GRAPHTFT" = "on" ] || [ "$graphtft" = "on" ]; then
+ echo "GRAPHTFT = 1"
+ else
+ echo "#GRAPHTFT = 1"
+ fi
+
+ if [ "$HARDLINKCUTTER" = "on" ]; then
+ echo "HARDLINKCUTTER = 1"
+ else
+ echo "#HARDLINKCUTTER = 1"
+ fi
+
+ if [ "$JUMPPLAY" = "on" ]; then
+ echo "JUMPPLAY = 1"
+ else
+ echo "#JUMPPLAY = 1"
+ fi
+
+ if [ "$LIEMIEXT" = "on" ]; then
+ echo "LIEMIEXT = 1"
+ else
+ echo "#LIEMIEXT = 1"
+ fi
+
+ if [ "$LIRCSETTINGS" = "on" ]; then # || [ "$USELIRC" = "on" ]
+ echo "LIRCSETTINGS = 1"
+ else
+ echo "#LIRCSETTINGS = 1"
+ fi
+
+ if [ "$LIVEBUFFER" = "on" ]; then
+ echo "LIVEBUFFER = 1"
+ else
+ echo "#LIVEBUFFER = 1"
+ fi
+
+ if [ "$LNBSHARE" = "on" ]; then
+ echo "LNBSHARE = 1"
+ else
+ echo "#LNBSHARE = 1"
+ fi
+
+ if [ "$MAINMENUHOOKS" = "on" ] || [ "$epgsearch" = "on" ] || [ "$extrecmenu" = "on" ] || [ "$remoteosd" = "on" ] || [ "$remotetimers" = "on" ]; then
+ echo "MAINMENUHOOKS = 1"
+ else
+ echo "#MAINMENUHOOKS = 1"
+ fi
+
+ if [ "$MENUORG" = "on" ] || [ "$menuorg" = "on" ]; then
+ SETUP="off"
+ setup="off"
+ SUBMENU="off"
+ submenu="off"
+ echo "MENUORG = 1"
+ else
+ echo "#MENUORG = 1"
+ fi
+
+ if [ "$NOEPG" = "on" ] || [ "$noepgmenu" = "on" ]; then
+ echo "NOEPG = 1"
+ else
+ echo "#NOEPG = 1"
+ fi
+
+ if [ "$OSDMAXITEMS" = "on" ] || [ "$text2skin" = "on" ]; then
+ echo "OSDMAXITEMS = 1"
+ else
+ echo "#OSDMAXITEMS = 1"
+ fi
+
+ if [ "$PARENTALRATING" = "on" ]; then
+ echo "PARENTALRATING = 1"
+ else
+ echo "#PARENTALRATING = 1"
+ fi
+
+ if [ "$PINPLUGIN" = "on" ] || [ "$pin" = "on" ]; then
+ echo "PINPLUGIN = 1"
+ else
+ echo "#PINPLUGIN = 1"
+ fi
+
+ if [ "$PLUGINAPI" = "on" ]; then
+ echo "PLUGINAPI = 1"
+ else
+ echo "#PLUGINAPI = 1"
+ fi
+
+ if [ "$PLUGINMISSING" = "on" ]; then
+ echo "PLUGINMISSING = 1"
+ else
+ echo "#PLUGINMISSING = 1"
+ fi
+
+ if [ "$PLUGINPARAM" = "on" ] || [ "$iptv" = "on" ]; then
+ echo "PLUGINPARAM = 1"
+ else
+ echo "#PLUGINPARAM = 1"
+ fi
+
+ if [ "$REELPLUGIN" = "on" ] || [ "$reelbox" = "on" ]; then
+ echo "REELPLUGIN = 1"
+ else
+ echo "#REELPLUGIN = 1"
+ fi
+
+ if [ "$ROTOR" = "on" ] || [ "$rotor" = "on" ]; then
+ echo "ROTOR = 1"
+ else
+ echo "#ROTOR = 1"
+ fi
+
+ if [ "$SETTIME" = "on" ]; then
+ echo "SETTIME = 1"
+ else
+ echo "#SETTIME = 1"
+ fi
+
+ if [ "$SETUP" = "on" ] || [ "$setup" = "on" ]; then
+ echo "SETUP = 1"
+ SUBMENU="off"
+ submenu="off"
+ else
+ echo "#SETUP = 1"
+ fi
+
+ if [ "$SOFTOSD" = "on" ]; then
+ echo "SOFTOSD = 1"
+ else
+ echo "#SOFTOSD = 1"
+ fi
+
+ if [ "$SOURCECAPS" = "on" ]; then
+ echo "SOURCECAPS = 1"
+ else
+ echo "#SOURCECAPS = 1"
+ fi
+
+ if [ "$SORTRECORDS" = "on" ]; then
+ echo "SORTRECORDS = 1"
+ else
+ echo "#SORTRECORDS = 1"
+ fi
+
+ if [ "$SUBMENU" = "on" ] || [ "$submenu" = "on" ]; then
+ echo "SUBMENU = 1"
+ else
+ echo "#SUBMENU = 1"
+ fi
+
+ if [ "$STREAMDEVEXT" = "on" ]; then
+ echo "STREAMDEVEXT = 1"
+ else
+ echo "#STREAMDEVEXT = 1"
+ fi
+
+ if [ "$SYNCEARLY" = "on" ]; then
+ echo "SYNCEARLY = 1"
+ else
+ echo "#SYNCEARLY = 1"
+ fi
+
+ if [ "$TIMERCMD" = "on" ]; then
+ echo "TIMERCMD = 1"
+ else
+ echo "#TIMERCMD = 1"
+ fi
+
+ if [ "$TIMERINFO" = "on" ]; then
+ echo "TIMERINFO = 1"
+ else
+ echo "#TIMERINFO = 1"
+ fi
+
+ if [ "$TTXTSUBS" = "on" ] || [ "$ttxtsubs" = "on" ]; then
+ echo "TTXTSUBS = 1"
+ else
+ echo "#TTXTSUBS = 1"
+ fi
+
+ if [ "$VALIDINPUT" = "on" ]; then
+ echo "VALIDINPUT = 1"
+ else
+ echo "#VALIDINPUT = 1"
+ fi
+
+ if [ "$VOLCTRL" = "on" ]; then
+ echo "VOLCTRL = 1"
+ else
+ echo "#VOLCTRL = 1"
+ fi
+
+ if [ "$WAREAGLEICON" = "on" ]; then
+ echo "WAREAGLEICON = 1"
+ else
+ echo "#WAREAGLEICON = 1"
+ fi
+
+ if [ "$YAEPG" = "on" ] || [ "$yaepg" = "on" ]; then
+ echo "YAEPG = 1"
+ else
+ echo "#YAEPG = 1"
+ fi
+ fi
+ echo ""
+ echo ""
+ echo "### You don't need to touch the following:"
+ echo ""
+ echo "ifdef DVBDIR"
+ echo "INCLUDES += -I\$(DVBDIR)/include"
+ echo "endif"
+ echo ""
+ if [ "$PATCHLEVEL" = "EXTENSIONS" ]; then
+ echo "ifdef ANALOGTV"
+ echo "DEFINES += -DUSE_ANALOGTV"
+ echo "endif"
+ echo ""
+ echo "ifdef ATSC"
+ echo "DEFINES += -DUSE_ATSC"
+ echo "endif"
+ echo ""
+ echo "ifdef CHANNELSCAN"
+ echo "DEFINES += -DUSE_CHANNELSCAN"
+ echo "endif"
+ echo ""
+ echo "ifdef CMDRECCMDI18N"
+ echo "DEFINES += -DUSE_CMDRECCMDI18N"
+ echo "endif"
+ echo ""
+ echo "ifdef CMDSUBMENU"
+ echo "DEFINES += -DUSE_CMDSUBMENU"
+ echo "endif"
+ echo ""
+ echo "ifdef CUTTERLIMIT"
+ echo "DEFINES += -DUSE_CUTTERLIMIT"
+ echo "endif"
+ echo ""
+ echo "ifdef CUTTERQUEUE"
+ echo "DEFINES += -DUSE_CUTTERQUEUE"
+ echo "endif"
+ echo ""
+ echo "ifdef CUTTIME"
+ echo "DEFINES += -DUSE_CUTTIME"
+ echo "endif"
+ echo ""
+ echo "ifdef DELTIMESHIFTREC"
+ echo "DEFINES += -DUSE_DELTIMESHIFTREC"
+ echo "endif"
+ echo ""
+ echo "ifdef DDEPGENTRY"
+ echo "DEFINES += -DUSE_DDEPGENTRY"
+ echo "endif"
+ echo ""
+ echo "ifdef DOLBYINREC"
+ echo "DEFINES += -DUSE_DOLBYINREC"
+ echo "endif"
+ echo ""
+ echo "ifdef DVBPLAYER"
+ echo "DEFINES += -DUSE_DVBPLAYER"
+ echo "endif"
+ echo ""
+ echo "ifdef DVBSETUP"
+ echo "DEFINES += -DUSE_DVBSETUP"
+ echo "endif"
+ echo ""
+ echo "ifdef DVDARCHIVE"
+ echo "ifdef LIEMIEXT"
+ echo "DEFINES += -DUSE_DVDARCHIVE"
+ echo "endif"
+ echo "endif"
+ echo ""
+ echo "ifdef DVLRECSCRIPTADDON"
+ echo "DEFINES += -DUSE_DVLRECSCRIPTADDON"
+ echo "endif"
+ echo ""
+ echo "ifdef DVLVIDPREFER"
+ echo "DEFINES += -DUSE_DVLVIDPREFER"
+ echo "endif"
+ echo ""
+ echo "ifdef DVLFRIENDLYFNAMES"
+ echo "DEFINES += -DUSE_DVLFRIENDLYFNAMES"
+ echo "endif"
+ echo ""
+ echo "ifdef EM84XX"
+ echo "DEFINES += -DUSE_EM84XX"
+ echo "endif"
+ echo ""
+ echo "ifdef GOTOX"
+ echo "DEFINES += -DUSE_GOTOX"
+ echo "endif"
+ echo ""
+ echo "ifdef GRAPHTFT"
+ echo "DEFINES += -DUSE_GRAPHTFT"
+ echo "endif"
+ echo ""
+ echo "ifdef HARDLINKCUTTER"
+ echo "DEFINES += -DUSE_HARDLINKCUTTER"
+ echo "endif"
+ echo ""
+ echo "ifdef JUMPPLAY"
+ echo "DEFINES += -DUSE_JUMPPLAY"
+ echo "endif"
+ echo ""
+ echo "ifdef LIEMIEXT"
+ echo "DEFINES += -DUSE_LIEMIEXT"
+ echo "endif"
+ echo ""
+ echo "ifdef LIRCSETTINGS"
+ echo "DEFINES += -DUSE_LIRCSETTINGS"
+ echo "endif"
+ echo ""
+ echo "ifdef LIVEBUFFER"
+ echo "DEFINES += -DUSE_LIVEBUFFER"
+ echo "endif"
+ echo ""
+ echo "ifdef LNBSHARE"
+ echo "DEFINES += -DUSE_LNBSHARE"
+ echo "endif"
+ echo ""
+ echo "ifdef MAINMENUHOOKS"
+ echo "DEFINES += -DUSE_MAINMENUHOOKS"
+ echo "endif"
+ echo ""
+ echo "ifdef MENUORG"
+ echo "DEFINES += -DUSE_MENUORG"
+ echo "else"
+ echo "ifdef SETUP"
+ echo "DEFINES += -DUSE_SETUP"
+ echo "else"
+ echo "ifdef SUBMENU"
+ echo "DEFINES += -DUSE_SUBMENU"
+ echo "endif"
+ echo "endif"
+ echo "endif"
+ echo ""
+ echo "ifdef NOEPG"
+ echo "DEFINES += -DUSE_NOEPG"
+ echo "endif"
+ echo ""
+ echo "ifdef OSDMAXITEMS"
+ echo "DEFINES += -DUSE_OSDMAXITEMS"
+ echo "endif"
+ echo ""
+ echo "ifdef PARENTALRATING"
+ echo "DEFINES += -DUSE_PARENTALRATING"
+ echo "endif"
+ echo ""
+ echo "ifdef PINPLUGIN"
+ echo "DEFINES += -DUSE_PINPLUGIN"
+ echo "endif"
+ echo ""
+ echo "ifdef PLUGINMISSING"
+ echo "DEFINES += -DUSE_PLUGINMISSING"
+ echo "endif"
+ echo ""
+ echo "ifdef PLUGINPARAM"
+ echo "DEFINES += -DUSE_PLUGINPARAM"
+ echo "endif"
+ echo ""
+ echo "ifdef REELPLUGIN"
+ echo "DEFINES += -DUSE_REELPLUGIN"
+ echo "endif"
+ echo ""
+ echo "ifdef ROTOR"
+ echo "DEFINES += -DUSE_ROTOR"
+ echo "endif"
+ echo ""
+ echo "ifdef SETTIME"
+ echo "DEFINES += -DUSE_SETTIME"
+ echo "endif"
+ echo ""
+ echo "ifdef SOFTOSD"
+ echo "DEFINES += -DUSE_SOFTOSD"
+ echo "endif"
+ echo ""
+ echo "ifdef SOURCECAPS"
+ echo "DEFINES += -DUSE_SOURCECAPS"
+ echo "endif"
+ echo ""
+ echo "ifdef SORTRECORDS"
+ echo "ifdef LIEMIEXT"
+ echo "DEFINES += -DUSE_SORTRECORDS"
+ echo "endif"
+ echo "endif"
+ echo ""
+ echo "ifdef STREAMDEVEXT"
+ echo "DEFINES += -DUSE_STREAMDEVEXT"
+ echo "endif"
+ echo ""
+ echo "ifdef SYNCEARLY"
+ echo "DEFINES += -DUSE_SYNCEARLY"
+ echo "endif"
+ echo ""
+ echo "ifdef TIMERCMD"
+ echo "DEFINES += -DUSE_TIMERCMD"
+ echo "endif"
+ echo ""
+ echo "ifdef TIMERINFO"
+ echo "DEFINES += -DUSE_TIMERINFO"
+ echo "endif"
+ echo ""
+ echo "ifdef TTXTSUBS"
+ echo "DEFINES += -DUSE_TTXTSUBS"
+ echo "endif"
+ echo ""
+ echo "ifdef VALIDINPUT"
+ echo "DEFINES += -DUSE_VALIDINPUT"
+ echo "endif"
+ echo ""
+ echo "ifdef VOLCTRL"
+ echo "DEFINES += -DUSE_VOLCTRL"
+ echo "endif"
+ echo ""
+ echo "ifdef WAREAGLEICON"
+ echo "DEFINES += -DUSE_WAREAGLEICON"
+ echo "endif"
+ echo ""
+ echo "ifdef YAEPG"
+ echo "DEFINES += -DUSE_YAEPG"
+ echo "endif"
+ echo ""
+ else
+ if [ "$setup" = "on" ] || [ "$SETUP" = "on" ]; then
+ echo "DEFINES += -DUSE_LIVEBUFFER"
+ echo ""
+ fi
+ fi
+ } > $MAKECONFIG
+}
+make_makeconfig
+
+## patch VDR
+cd $SOURCEDIR/VDR
+
+# download vdr-maintenance patches
+if [ -n "$MTPATCHES" ]; then
+ for n in $MTPATCHES; do
+ if [ -f $DIR/maintenance/vdr-$VDRVERSION-$n.diff ]; then
+ log "vdr-$VDRVERSION-$n.diff gefunden"
+ else
+ wget -q --tries=1 "$VDRWEB/Developer/vdr-$VDRVERSION-$n.diff" --directory-prefix="$DIR/maintenance"
+ [ ! -f $DIR/maintenance/vdr-$VDRVERSION-$n.diff ] && break
+ log "vdr-$VDRVERSION-$n.diff geladen"
+ fi
+ done
+fi
+
+# apply vdr-maintenance patches
+for i in `ls $DIR/maintenance | grep "vdr-$VDRVERSION-.*.diff$"`; do
+ log "apply $i"
+ patch -f -p 1 < $DIR/maintenance/$i
+done
+
+# check if dvb-api-wrapper is needed
+if [ ${VDRVERSION:2:1} -gt 6 ] && [ "$PATCHLEVEL" != "PLAIN" ]; then
+ if [ $DVB_API_VERSION -lt 5 -a ${VDRVERSION:4:1} -gt 1 -o ${VDRVERSION:2:1} -gt 7 ] ||
+ [ $DVB_API_VERSION -eq 3 -a $DVB_API_VERSION_MINOR -lt 3 ]; then
+ api_wrapper_patch=$(ls -r $DIR | grep -m1 "^vdr-$VDRVERSION.*s2apiwrapper.*.diff$")
+ [ -n "$api_wrapper_patch" ] || api_wrapper_patch=$(ls -r $DIR | grep -m1 "^vdr-1.7.*s2apiwrapper.*.diff$")
+ if [ -n "$api_wrapper_patch" ]; then
+ log "apply $api_wrapper_patch"
+ patch -f -p 1 < $DIR/$api_wrapper_patch
+ else
+ echo "- DVB_API_VERSION < 5 und api_wrapper_patch nicht gefunden." >> $error_file
+ log "DVB_API_VERSION < 5 und api_wrapper_patch nicht gefunden"
+ exit 1
+ fi
+ fi
+fi
+
+# apply extensions
+if [ "$PATCHLEVEL" = "EXTENSIONS" ]; then
+ extensions_patch=$(ls -r $DIR/extensions | grep -m1 "^vdr-${VDRVERSION}_extensions.diff$")
+ [ -n "$extensions_patch" ] || extensions_patch=$(ls -r $DIR/extensions | grep -m1 "^vdr-${VDRVERSION}.*_extensions.diff$")
+ [ -n "$extensions_patch" ] || extensions_patch=$(ls -r $DIR/extensions | grep -m1 "^vdr-.*_extensions.diff$")
+ if [ -n "$extensions_patch" ]; then
+ log "apply $extensions_patch"
+ patch -f -p 1 < $DIR/extensions/$extensions_patch
+ # search for more extensions
+ for i in `ls $DIR/extensions | grep "^vdr-${VDRVERSION}.*-ext.*_.*.diff$"`; do
+ log "apply $i"
+ patch -f -p 1 < $DIR/extensions/$i
+ done
+ else
+ echo "- Extensions-Patch nicht gefunden." >> $error_file
+ log "Extensions-Patch nicht gefunden"
+ exit 1
+ fi
+fi
+
+if [ "$PATCHLEVEL" != "PLAIN" ]; then
+ # search for *.bz2
+ for i in `ls $DIR/patches | grep ".bz2$"`; do
+ log "apply $i"
+ bzcat $DIR/patches/$i | patch -f -p 1
+ done
+ # search for *.diff
+ for i in `ls $DIR/patches | grep ".diff$"`; do
+ log "apply $i"
+ patch -f -p 1 < $DIR/patches/$i
+ done
+ # search for *.patch
+ for i in `ls $DIR/patches | grep ".patch$"`; do
+ log "apply $i"
+ patch -f -p 1 < $DIR/patches/$i
+ done
+fi
+
+# remove default plugins
+if [ "$vdr_plugins" = "off" ]; then
+ rm -rf $SOURCEDIR/VDR/PLUGINS/src/hello
+ rm -rf $SOURCEDIR/VDR/PLUGINS/src/osddemo
+ rm -rf $SOURCEDIR/VDR/PLUGINS/src/pictures
+ rm -rf $SOURCEDIR/VDR/PLUGINS/src/servicedemo
+ rm -rf $SOURCEDIR/VDR/PLUGINS/src/skincurses
+ rm -rf $SOURCEDIR/VDR/PLUGINS/src/sky
+ rm -rf $SOURCEDIR/VDR/PLUGINS/src/status
+ rm -rf $SOURCEDIR/VDR/PLUGINS/src/svdrpdemo
+fi
+
+# make VDR
+log "****************************************"
+log "Erstellen des VDR ..."
+log "****************************************"
+cd $SOURCEDIR/VDR
+make $VDROPTIONS
+
+# copy instead of make install
+if ! cp -f vdr $VDRPRG ; then
+ log "****************************************"
+ log "Fehler! - VDR wurde nicht erstellt."
+ log "****************************************"
+ echo "- $VDRPRG konnte nicht erstellt werden." >> $error_file
+ exit 1
+fi
+
+# copy locale
+if [ -d $SOURCEDIR/VDR/locale ]; then
+ [ ! -d $VDRLOCDIR ] && mkdir -p $VDRLOCDIR && log "Erstelle $VDRLOCDIR"
+ cp -fR $SOURCEDIR/VDR/locale/* $VDRLOCDIR
+fi
+
+# stop if VDRUPDATE=on
+if [ "$VDRUPDATE" = "on" ]; then
+ log "****************************************"
+ log "Aktualisierung des VDR erfolgreich."
+ log "****************************************"
+ # delete error file
+ [ -f $error_file ] && rm -f $error_file
+ exit 0
+fi
+
+# copy / create files
+function make_files() {
+ # kopiere man1 und man5
+ gzip -c vdr.1 > $VDRMANDIR/man1/vdr.1.gz
+ gzip -c vdr.5 > $VDRMANDIR/man5/vdr.5.gz
+ # vdr-config dateien kopieren
+ cd $SOURCEDIR/VDR
+ cp -f svdrpsend.pl $VDRBINDIR
+ cp -f *.conf $VDRCONFDIR
+ # config dateien erstellen
+ cd $SOURCEDIR/x-vdr/vdr
+ ./make-conf.sh
+ # eigene config dateien kopieren
+ cd $SOURCEDIR/x-vdr/vdr/conf
+ cp -f *.conf $VDRCONFDIR
+ cp -f _xinitrc $VDRCONFDIR/.xinitrc
+ # Kanallisten kopieren
+ [ -d $SOURCEDIR/x-vdr/extra/channellists ] && cp -fR $SOURCEDIR/x-vdr/extra/channellists $VDRCONFDIR
+ # eigene scripte kopieren
+ cd $SOURCEDIR/x-vdr/vdr/scripts
+ # runvdr
+ cp -f runvdr $VDRBINDIR
+ # dvd-bc
+ cp -f burn-bc $VDRBINDIR
+ # vdrplayer
+ cp -f vdrplayer $VDRBINDIR
+ # vdr2root
+ cp -f vdr2root $VDRSCRIPTDIR
+ # vdrmount
+ cp -f vdrmount $VDRSCRIPTDIR
+ # noad
+ cp -f vdrnoad $VDRSCRIPTDIR
+ # shutdown
+ cp -f vdrshutdown $VDRSCRIPTDIR
+ # reccmds
+ cp -f vdrreccmds $VDRSCRIPTDIR
+ # settime
+ cp -f vdrsettime $VDRSCRIPTDIR
+ # vdrsetup
+ cp -f vdrsetup $VDRSCRIPTDIR
+ # vdrreaddvd
+ cp -f vdrreaddvd $VDRSCRIPTDIR
+ # vdrwritedvd
+ cp -f vdrwritedvd $VDRSCRIPTDIR
+ # timerrep.sh
+ cp -f timerrep.sh $VDRSCRIPTDIR
+ # kopiere vdr init script
+ cp -f vdr /etc/init.d
+
+ # rechte setzen
+ chmod 0755 $VDRBINDIR/runvdr
+ chmod 0755 $VDRBINDIR/burn-bc
+ chmod 0755 $VDRBINDIR/vdrplayer
+ chmod 0755 /etc/init.d/vdr
+ chmod -R 0744 $VDRSCRIPTDIR
+
+ # mkdir DVD-VCD
+ if [ ! -d $MEDIADIR/DVD-VCD ]; then
+ mkdir -p $MEDIADIR/DVD-VCD
+ touch $MEDIADIR/DVD-VCD/DVD $MEDIADIR/DVD-VCD/VCD
+ chmod 0666 $MEDIADIR/DVD-VCD/DVD $MEDIADIR/DVD-VCD/VCD
+ fi
+
+ chown -R $VDRUSER:$VDRGROUP $VDRCONFDIR $VDRSCRIPTDIR $VDRVARDIR $VIDEODIR $MUSICDIR $DIVXDIR $DVDISODIR $MEDIADIR $PICTUREDIR
+}
+make_files
+
+# autostart VDR
+[ "$AUTOSTART" = "on" ] && make_autostart
+
+cd $SOURCEDIR
+
+# create /etc/default/vdr
+make_default
+log "****************************************"
+log "VDR Installation erfolgreich."
+log "****************************************"
+
+# delete error file
+[ -f $error_file ] && rm -f $error_file
+
+exit 0
+
diff --git a/vdr/make-conf.sh b/vdr/make-conf.sh
new file mode 100644
index 0000000..00e4088
--- /dev/null
+++ b/vdr/make-conf.sh
@@ -0,0 +1,215 @@
+#!/bin/sh
+
+# x-vdr (Installations-Skript fuer einen VDR mit Debian als Basis)
+# von Marc Wernecke - www.zulu-entertainment.de
+# 29.07.2008
+
+# defaults
+source ./../x-vdr.conf
+source ./../setup.conf
+#VDRSCRIPTDIR="/usr/lib/vdr/scripts"
+#VDRCONFDIR="."
+
+
+# Function
+function make_config() {
+# create commands.conf
+{
+echo "# $VDRCONFDIR/commands.conf
+# x-vdr `date '+%F %T'`
+#
+
+V D R : echo true
+- Aktualisiere Liste der Aufnahmen : sudo $VDRSCRIPTDIR/vdr2root video-update ; echo \"$VDRBINDIR/svdrpsend.pl HITK Red\"|at now
+- Loeschen der EPG daten? : echo \"$VDRBINDIR/svdrpsend.pl CLRE\"|at now
+- VDR neustarten? : sudo $VDRSCRIPTDIR/vdr2root vdr-restart
+- VDR beenden? : sudo $VDRSCRIPTDIR/vdr2root vdr-stop
+
+S y s t e m : echo true
+- Zeige /var/log/messages : sudo $VDRSCRIPTDIR/vdr2root show-log
+- Saeubere /var/log/messages : sudo $VDRSCRIPTDIR/vdr2root clean-log
+- Belegung der Laufwerke : df -h|awk '/%/ { printf(\"%4.4s : %5.5s : %s\n\",\$5,\$4,\$6) }'
+- Starte SSH? : sudo $VDRSCRIPTDIR/vdr2root ssh-start
+- SSH ausschalten? : sudo $VDRSCRIPTDIR/vdr2root ssh-stop
+- System neustarten? : sudo $VDRSCRIPTDIR/vdr2root reboot
+- System ausschalten? : echo \"$VDRBINDIR/svdrpsend.pl HITK Power\"|at now
+"
+
+if [ "$burn" = "on" ]; then
+echo "
+B u r n - B C : echo true
+- Blend Only : echo \"$VDRBINDIR/burn-bc -f draw-storke -w default\"|at now
+- Simple Cropping : echo \"$VDRBINDIR/burn-bc -f 'crop resize draw-storke unsharp' -w default\"|at now
+- Zoom Center : echo \"$VDRBINDIR/burn-bc -f 'crop zoom-center resize draw-storke unsharp' -w default\"|at now
+- Zoom Left : echo \"$VDRBINDIR/burn-bc -f 'crop zoom-left resize draw-storke unsharp' -w default\"|at now
+- Zoom Right : echo \"$VDRBINDIR/burn-bc -f 'crop zoom-right resize draw-storke unsharp' -w default\"|at now
+- Serial Foto : echo \"$VDRBINDIR/burn-bc -f 'crop zoom-center resize draw-storke serial-foto unsharp' -w default\"|at now
+- Serial Movie : echo \"$VDRBINDIR/burn-bc -f 'crop zoom-center resize draw-storke serial-movie unsharp' -w default\"|at now
+- Big Serial Movie : echo \"$VDRBINDIR/burn-bc -f 'crop zoom-center resize draw-bigsize serial-movie unsharp' -w default\"|at now
+- 16x9 -> 4x3 : echo \"$VDRBINDIR/burn-bc -f 'zoom-center-16x9 resize draw-storke unsharp' -w default\"|at now
+"
+fi
+
+if [ -e "$DVDBURNER" ]; then
+echo "
+eject dvd : eject $DVDBURNER
+"
+fi
+
+if [ "$vdrconvert" = "on" ]; then
+echo "
+V D R C o n v e r t - SVCD : echo true
+- Zu SVCD-Liste : ${PREFIX}/bin/ins.sh vdr2svcd
+- Aktiviere SVCD-Liste : ${PREFIX}/bin/convstart.sh vdr2svcd
+- Entferne von SVCD-Liste? : ${PREFIX}/bin/del.sh vdr2svcd
+- Zeige SVCD-Liste : ${PREFIX}/bin/cap.sh vdr2svcd
+V D R C o n v e r t - VCD : echo true
+- Zu VCD-Liste : ${PREFIX}/bin/ins.sh vdr2vcd
+- Aktiviere VCD-Liste : ${PREFIX}/bin/convstart.sh vdr2vcd
+- Entferne von VCD-Liste? : ${PREFIX}/bin/del.sh vdr2vcd
+- Zeige VCD-Liste : ${PREFIX}/bin/cap.sh vdr2vcd
+V D R C o n v e r t - DIVX : echo true
+- Zu DivX-Liste : ${PREFIX}/bin/ins.sh vdr2divx
+- Aktiviere DivX : ${PREFIX}/bin/convstart.sh vdr2divx
+- Entferne von DivX-Liste? : ${PREFIX}/bin/del.sh vdr2divx
+- Zeige DivX-Liste : ${PREFIX}/bin/cap.sh vdr2divx
+V D R C o n v e r t - Quickmodus : echo true
+- Erstelle DivX sofort? : ${PREFIX}/bin/convnow.sh vdr2divx
+- Erstelle DVD sofort? : ${PREFIX}/bin/convnow.sh vdr2dvd
+- Erstelle Handy sofort? : ${PREFIX}/bin/convnow.sh vdr2handy
+- Erstelle PDA sofort? : ${PREFIX}/bin/convnow.sh vdr2pda
+- Erstelle SVCD sofort? : ${PREFIX}/bin/convnow.sh vdr2svcd
+- Erstelle VCD sofort? : ${PREFIX}/bin/convnow.sh vdr2vcd
+- Erstelle Mpeg ( sync )? : ${PREFIX}/bin/convnow.sh vdr2mpg
+- Erstelle Mp3? : ${PREFIX}/bin/convnow.sh vdr2mp3
+- Erstelle ogg? : ${PREFIX}/bin/convnow.sh vdr2ogg
+- Erstelle AC3? : ${PREFIX}/bin/convnow.sh vdr2ac3
+"
+fi
+
+if [ "$vdrrip" = "on" ]; then
+echo "
+vdrrip starten : echo \"$VDRSCRIPTDIR/queuehandler.sh $VDRCONFDIR/plugins/queue.vdrrip /tmp\"|at now
+"
+fi
+
+echo "
+x-vdr setup : echo \"sudo /usr/lib/vdr/scripts/vdrsetup\"|at now
+"
+} > $VDRCONFDIR/commands.conf
+
+
+
+# reccmds.conf
+{
+echo "# $VDRCONFDIR/reccmds.conf
+# x-vdr `date '+%F %T'`
+#
+"
+
+if [ "$pin" = "on" ]; then
+echo "
+FSK Schutz hinzufuegen : $VDRSCRIPTDIR/fskprotect.sh protect
+FSK Schutz entfernen : $VDRSCRIPTDIR/fskprotect.sh unprotect
+"
+fi
+
+if [ "$noad" = "on" ]; then
+echo "
+Werbung markieren : $VDRSCRIPTDIR/vdrnoad -start
+M o r e N o a d : echo true
+- Run noad : $VDRSCRIPTDIR/vdrnoad -start
+- Run noad, all recordings (batch) : $VDRSCRIPTDIR/vdrnoad -batch
+- View Cut list : $VDRSCRIPTDIR/vdrnoad -view
+- In prozess : $VDRSCRIPTDIR/vdrnoad -count
+- Stop noad (killpid) : $VDRSCRIPTDIR/vdrnoad -killpid
+- Stop noad (killall)? : $VDRSCRIPTDIR/vdrnoad -killall
+- Remove marks : $VDRSCRIPTDIR/vdrnoad -marks
+- Remove pid : $VDRSCRIPTDIR/vdrnoad -pid
+- Remove all marks : $VDRSCRIPTDIR/vdrnoad -marks.vdr
+- Remove all pids : $VDRSCRIPTDIR/vdrnoad -noad.pid
+"
+fi
+
+if [ "$vdrconvert" = "on" ]; then
+echo "
+V D R C o n v e r t : echo true
+- Show VDRConvert Status : ${PREFIX}/bin/status.sh
+- Import Audio CD : echo 1 > /var/spool/vdrconvert/ripcda
+- VDRConvert einschalten : /etc/init.d/vdrconvert start >/dev/null 2>&1
+- VDRConvert ausschalten : /etc/init.d/vdrconvert stop >/dev/null 2>&1
+"
+fi
+} > $VDRCONFDIR/reccmds.conf
+
+# timercmds.conf
+{
+echo "# $VDRCONFDIR/timercmds.conf
+# x-vdr `date '+%F %T'`
+#
+"
+
+if [ "$epgsearch" = "on" ]; then
+echo "
+Suche nach Wiederholungen : $VDRSCRIPTDIR/timerrep.sh 0
+Suche nach Wiederholungen mit Episodentitle : $VDRSCRIPTDIR/timerrep.sh 1
+"
+fi
+} > $VDRCONFDIR/timercmds.conf
+
+# setup.conf
+{
+echo "OSDHeight = 488
+OSDLanguage = 1
+OSDLeft = 62
+OSDMessageTime = 1
+OSDSkin = EnigmaNG
+OSDTheme = default
+OSDTop = 40
+OSDWidth = 584
+dvd.AudioLanguage = 1
+dvd.Gain = 4
+dvd.HideMainMenu = 0
+dvd.MenuLanguage = 1
+dvd.PlayerRCE = 2
+dvd.ReadAHead = 0
+dvd.ShowSubtitles = 0
+dvd.SpuLanguage = 1
+dvdselect.DVDOutputDir = $DVDISODIR
+dvdselect.DVDReadScript = $VDRSCRIPTDIR/vdrreaddvd
+dvdselect.DVDWriteScript = $VDRSCRIPTDIR/vdrwritedvd
+dvdselect.ImageDir = $DVDISODIR
+dvdselect.NameDevice = /dev/dvd
+dvdselect.NameOrgDevice = /dev/hdc
+dvdselect.ResetDVDLink = 1
+femon.Skin = 0
+femon.Theme = 4
+mediamvp.dongle_version = 22146
+osdpip.ColorDepth = 3
+osdpip.CropBottom = 5
+osdpip.CropLeft = 5
+osdpip.CropRight = 5
+osdpip.CropTop = 5
+osdpip.FrameDrop = -1
+osdpip.FrameMode = 2
+osdpip.InfoPosition = 2
+osdpip.InfoWidth = 400
+osdpip.ShowInfo = 1
+osdpip.Size = 2
+osdpip.SwapFfmpeg = 1
+osdpip.XPosition = 50
+osdpip.YPosition = 50
+setup.DirectMenu = 1
+setup.EntryPrefix = :
+setup.MenuSuffix =
+setup.ReturnValue = 1
+ttxtsubs.MainMenuEntry = 3
+"
+} > $VDRCONFDIR/setup.conf
+
+} # Function End
+
+# Function Call
+make_config
+
+
diff --git a/vdr/patchlevel.conf b/vdr/patchlevel.conf
new file mode 100644
index 0000000..ee1a848
--- /dev/null
+++ b/vdr/patchlevel.conf
@@ -0,0 +1,60 @@
+### x-vdr patchlevel.conf
+# EXTENSIONS or PLAIN
+PATCHLEVEL="EXTENSIONS"
+
+### VDR-Extensions:
+# Plugin patches will be added automaticly if a corresponding plugin is enabled in x-vdr
+# Comment the patches you don't need
+# DVDCHAPJUMP needs DVDARCHIVE enabled
+# DVDARCHIVE needs LIEMIEXT enabled
+# SORTRECORDS needs LIEMIEXT enabled
+# you can only enable one of MENUORG SUBMENU SETUP
+
+ANALOGTV="off"
+ATSC="off"
+CMDRECCMDI18N="off"
+CMDSUBMENU="on"
+CUTTERLIMIT="off"
+CUTTERQUEUE="on"
+CUTTIME="on"
+DDEPGENTRY="on"
+DOLBYINREC="on"
+DVBPLAYER="off"
+DVBSETUP="on"
+DVDARCHIVE="off"
+DVDCHAPJUMP="off"
+DVLFRIENDLYFNAMES="off"
+DVLRECSCRIPTADDON="on"
+DVLVIDPREFER="off"
+EM84XX="off"
+GRAPHTFT="off"
+HARDLINKCUTTER="off"
+JUMPPLAY="on"
+LIEMIEXT="on"
+LIRCSETTINGS="off"
+LIVEBUFFER="off"
+LNBSHARE="off"
+MAINMENUHOOKS="off"
+MENUORG="off"
+NOEPG="off"
+OSDMAXITEMS="off"
+PARENTALRATING="off"
+PINPLUGIN="off"
+PLUGINAPI="on"
+PLUGINMISSING="on"
+PLUGINPARAM="off"
+ROTOR="off"
+SETTIME="on"
+SETUP="off"
+SOFTOSD="off"
+SOURCECAPS="off"
+SORTRECORDS="on"
+STREAMDEVEXT="off"
+SYNCEARLY="on"
+TIMERCMD="off"
+TIMERINFO="off"
+TTXTSUBS="off"
+VALIDINPUT="off"
+VOLCTRL="off"
+WAREAGLEICON="on"
+YAEPG="off"
diff --git a/vdr/scripts/README.DE_burn-bc b/vdr/scripts/README.DE_burn-bc
new file mode 100644
index 0000000..831f9e1
--- /dev/null
+++ b/vdr/scripts/README.DE_burn-bc
@@ -0,0 +1,142 @@
+burn-bc (burn background creator 0.0.5) - readme 2007-07-01
+===================================================
+Beschreibung:
+=================
+burn-bc ist ein Skript zum erstellen von Hintergründen für das burn-plugin des VDR.
+Das Skript kann per Terminal, Maus (Xdialog) oder aus dem VDR-Menu bedient werden.
+Es können Bilder aus der eigenen Sammlung oder Screenshots der laufenden Sendung bzw. Aufzeichnung verwendet werden.
+Die Bilder werden mit auswählbaren Filtern bearbeitet und an die entsprechenden Stellen kopiert.
+Textfelder können gezeichnet oder als Template eingeblendet eingebelndet werden.
+
+=================
+Vorraussetzungen:
+=================
+ImageMagick 6.2.4
+svdrpsend.pl (zum erstellen der Screenshots)
+sharutils (zum erstellen der Screenshots)
+Xdialog (optional)
+at (optional)
+
+=================
+Installation:
+=================
+Am einfachsten ist es, burn-bc nach /usr/bin zu kopieren und ausführbar zu machen.
+# terminal
+cp ./burn-bc /usr/bin
+chmod 755 /usr/bin/burn-bc
+
+Das Skript kann aber auch an jeder anderen Stelle abgelegt werden.
+Nur muss dann der Pfad beim aufrufen mit angegeben werden.
+
+Damit burn-bc aus dem VDR-Menu bedienbar ist, muss die commands.conf angepasst werden:
+burn-bc : echo "/usr/bin/burn-bc -f 'crop zoom-center resize draw-storke unsharp' -w default"|at now
+
+Wer auch eigene Bilder verwenden möchte, kann zum aufrufen das image-plugin benutzen und dafür die imagecmds.conf ergänzen:
+burn-bc : echo "/usr/bin/burn-bc -f 'draw-storke unsharp' -w default -i %s"|at now
+
+Weitere Beispiele gibt es weiter unten...
+
+=================
+Filter:
+=================
+# Optimierung
+crop -> Ränder abschneiden
+
+# Bildausschnitt
+zoom-center -> Zoom auf die Mitte des Bildes
+zoom-left -> Zoom auf die linke Seite des Bildes
+zoom-right -> Zoom auf die rechte Seite des Bildes
+zoom-center-16x9 -> Zoom auf die Mitte eines anamorphen 16/9 Bildes
+zoom-left-16x9 -> Zoom auf die linke Seite eines anamorphen 16/9 Bildes
+zoom-right-16x9 -> Zoom auf die rechte Seite eines anamorphen 16/9 Bildes
+letterbox-16x9 -> Erzeugt ein Letterbox Bild aus einem anamorphen 16/9 Bild
+
+# Optimierung (nach dem Ränder abschneiden oder zoomen notwendig)
+resize -> Skaliert das Bild auf 720x576
+
+# Textfelder
+comp-atop -> Legt ein Template über das Bild
+comp-blend -> Blendet ein Template in das Bild
+draw-bigsize -> Zeichnet zwei grosse, transparente Textfelder mit Rahmen über das Bild
+draw-normal -> Zeichnet zwei transparente Textfelder mit runde Ecken über das Bild
+draw-storke -> Zeichnet zwei transparente Textfelder mit runde Ecken und Rahmen über das Bild
+draw-user -> Zeichnet die Textfelder über das Bild (zum anpassen gedacht)
+
+# Fun
+serial-foto -> Legt eine Serie von vier bzw fünf (bei 16/9 Filmen) verkleinerten Screenshots über das Bild
+serial-movie -> Montiert eine Serie von fünf Screenshots zu einem Filmstreifen und legt diesen über das Bild
+tux -> Blendet ein Bild (das als Template "$targetdir/tux.png" vorliegen muss) in den unteren rechten Teil des Textfeldes ein.
+watermark -> Blendet einen Schriftzug in den unteren rechten Teil des Textfeldes ein.
+
+# Optimierung
+sharpen -> Leichtes anheben der Bildschärfe
+depth -> Reduziert die Farbtiefe auf 24bpp (damit das Bild mit dem Image-Plugin angezeigt werden kann)
+
+=================
+Tipps:
+=================
+Auf die Reihenfolge der Filter achten und nie mehr als notwendig verwenden.
+
+=================
+Argumente:
+=================
+burn-bc [-argument wert]
+Optionale Argumente sind:
+-f | -filter crop
+-f | -filter zoom-center|zoom-left|zoom-right|zoom-center-16x9|zoom-left-16x9|zoom-right-16x9|letterbox-16x9
+-f | -filter resize
+-f | -filter comp-atop|comp-blend|draw-bigsize|draw-normal|draw-storke|draw-user
+-f | -filter serial-foto|serial-movie
+-f | -filter tux
+-f | -filter sharpen
+-f | -filter depth
+-i | -image /path/to/image.ext
+-o | -osd on|off
+-t | -target /path/to/plugins/burn/menu-bg.png
+-T | -template /path/to/template.png
+-w | -watermark ?|default|"some text"
+-W | -width 720|768
+-x | -showfile on|off
+-d | -dialog
+-h | -help
+-v | -version
+
+Terminal:
+=================
+burn-bc -w default -f crop -f resize -f draw-storke'
+burn-bc -f 'crop zoom-center resize draw-storke unsharp depth' -o off -w default
+burn-bc -f draw-normal -w default -i ~/bilder/image.jpg
+
+Xdialog:
+=================
+burn-bc
+burn-bc -d -w " Dokumentation"
+burn-bc -d -w ?
+
+KDE Kontext-Menu (rechte Maustaste):
+burn-bc -d -w ? -i %f
+
+commands.conf
+=================
+d v d - b c : echo "sub menu"
+- Draw Only : echo "/usr/bin/burn-bc -f draw-storke -w default"|at now
+- Simple Cropping : echo "/usr/bin/burn-bc -f 'crop resize draw-storke unsharp' -w default"|at now
+- Zoom Center : echo "/usr/bin/burn-bc -f 'crop zoom-center resize draw-storke unsharp' -w default"|at now
+- Zoom Left : echo "/usr/bin/burn-bc -f 'crop zoom-left resize draw-storke unsharp' -w default"|at now
+- Zoom Right : echo "/usr/bin/burn-bc -f 'crop zoom-right resize draw-storke unsharp' -w default"|at now
+- Serial Foto : echo "/usr/bin/burn-bc -f 'crop zoom-center resize draw-storke serial-foto unsharp' -w default"|at now
+- Serial Movie : echo "/usr/bin/burn-bc -f 'crop zoom-center resize draw-storke serial-movie unsharp' -w default"|at now
+
+imagecmds.conf
+=================
+# ? - Bestätigung anfordern, (optional)
+# %s - Platzhalter für Dateinamen (optional)
+# burn-bc
+Draw Only : echo "/usr/bin/burn-bc -f draw-storke -w default -i %s"|at now
+Resize Only : echo "/usr/bin/burn-bc -f 'draw-storke unsharp' -w default -i %s"|at now
+Simple Cropping : echo "/usr/bin/burn-bc -f 'crop resize draw-storke unsharp' -w default -i %s"|at now
+Zoom Center : echo "/usr/bin/burn-bc -f 'zoom-center resize draw-storke unsharp' -w default -i %s"|at now
+Zoom Left : echo "/usr/bin/burn-bc -f 'crop zoom-left resize draw-storke unsharp' -w default -i %s"|at now
+Zoom Right : echo "/usr/bin/burn-bc -f 'crop zoom-right resize draw-storke unsharp' -w default -i %s"|at now
+Symlink anlegen : echo ln -fs '%s' /var/lib/vdr/plugins/burn/menu-bg.png | sh
+
diff --git a/vdr/scripts/burn-bc b/vdr/scripts/burn-bc
new file mode 100644
index 0000000..750b4d9
--- /dev/null
+++ b/vdr/scripts/burn-bc
@@ -0,0 +1,838 @@
+#! /bin/bash
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+script="burn-bc" #
+version="burn background creator 0.0.5" #
+# written by Marc Wernecke #
+# date: 2007-07-01 #
+# use: script [-argument value] #
+# optional arguments are: #
+# -f | -filter crop #
+# zoom-center|zoom-left|zoom-right| #
+# zoom-center-16x9|zoom-left-16x9|zoom-right-16x9|letterbox-16x9 #
+# resize #
+# comp-atop|comp-blend|draw-bigsize|draw-normal|draw-storke|draw-user #
+# serial-foto|serial-movie #
+# tux #
+# sharpen #
+# depth #
+# -i | -image /path/to/image.ext #
+# -t | -target /path/to/plugins/burn/menu-bg.png #
+# -T | -template /path/to/template.png #
+# -o | -osd on|off #
+# -w | -watermark ?|default|"some text" #
+# -W | -width 720|768 #
+# -x | -showfile on|off #
+# -d | -dialog #
+# -h | -help #
+# -v | -version #
+# #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# #
+# defaults: #
+FILTER="" # filter1 filter2 #
+IMAGE="" # /path/file #
+INFO="off" # on|off #
+OSDMSG="on" # on|off #
+TARGET="$HOME/plugins/burn/menu-bg.png" # /path/file.ext #
+TEMPLATE="template.png" # /path/file.ext #
+WATERMARK="off" # on|off #
+WIDTH="768" # 720|768 #
+WMTEXT=" powered by VDR" # "some text" #
+SHOWFILE="off" # on|off #
+TMPDIR="/tmp" # /path #
+SVDRPSEND="svdrpsend.pl" # /path/file.ext #
+# #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+args="$*"
+language="deu"
+
+# svdrpsend.pl
+TEST=$(which $SVDRPSEND)
+if [ ! "$TEST" ]; then
+ SVDRPSEND="$(grep "^VDRBINDIR=" /etc/default/vdr | cut -d "=" -f 2 | sed -e 's/\"//g')/svdrpsend.pl"
+ if [ ! -x "$SVDRPSEND" ]; then
+ echo "svdrpsend.pl not found"
+ exit 2
+ fi
+fi
+
+## FUNCTIONS
+function start_action() {
+ # target directory
+ test "${TARGET:0:1}" = "/"
+ if [ "x$?" != "x0" ]; then
+ echo "$script: TARGET must be an absolute path ..."
+ [ "$OSDMSG" = "on" ] && $SVDRPSEND "MESG $script: TARGET must be an absolute path"
+ exit 3
+ fi
+ targetdir="$(dirname $TARGET)"
+ if [ ! -d "$targetdir/$script" ]; then
+ mkdir -p "$targetdir/$script"
+ if [ ! -d "$targetdir/$script" ]; then
+ echo "$script: Could not create $targetdir/$script ..."
+ [ "$OSDMSG" = "on" ] && $SVDRPSEND "MESG $script: Could not create $targetdir/$script"
+ exit 3
+ fi
+ fi
+
+ # temp directory
+ test "${TMPDIR:0:1}" = "/"
+ if [ "x$?" != "x0" ]; then
+ echo "$script: TMPDIR must be an absolute path ..."
+ [ "$OSDMSG" = "on" ] && $SVDRPSEND "MESG $script: TMPDIR must be an absolute path"
+ exit 4
+ fi
+ if [ ! -d "$TMPDIR" ]; then
+ echo "$script: $TMPDIR not found ..."
+ [ "$OSDMSG" = "on" ] && $SVDRPSEND "MESG $script: $TMPDIR not found"
+ exit 4
+ fi
+ tmpdir="$TMPDIR/$$$script"
+ mkdir -p "$tmpdir"
+
+ # image
+ if [ -f "$IMAGE" ]; then
+ image=$(basename "$IMAGE")
+ convert -extent $(identify -format "%wx%h" "$IMAGE") "$IMAGE" "$tmpdir/$image"
+ else
+ image="burn-bg_$(date '+%F_%T').jpg"
+ IMAGE="$image" # this is for logging only
+ $SVDRPSEND grab - 100 $WIDTH 576 | \
+ sed -n -e 's/^216-//p' -e '1ibegin-base64 644 -' -e '$a====' | \
+ uudecode -o "$tmpdir/$image" # <- was passiert hier bei sed mit dem image?
+ BYTES=$(ls -l "$tmpdir/$image" | tr -s " " | cut -d " " -f 5)
+ if [ "$BYTES" = "0" ]; then
+ echo "$script: Snapshot is empty ..."
+ [ "$OSDMSG" = "on" ] && $SVDRPSEND "MESG $script: Snapshot is empty"
+ exit 5
+ fi
+ fi
+
+ # check if temp image exists
+ if [ ! -f "$tmpdir/$image" ]; then
+ echo "$script: Image not found ..."
+ [ "$OSDMSG" = "on" ] && $SVDRPSEND "MESG $script: Image not found"
+ exit 6
+ fi
+
+ # resize temp image to 720x576 if necessary
+ f_resize
+}
+
+# filter functions
+function f_comp() {
+ # template.png
+ if [ -f "$TEMPLATE" ]; then
+ template="$TEMPLATE"
+ elif [ -f "$targetdir/$TEMPLATE" ]; then
+ template="$targetdir/$TEMPLATE"
+ else
+ template="$targetdir/template.png"
+ fi
+ if [ ! -f "$template" ]; then
+ echo "$script: $template not found ..."
+ [ "$OSDMSG" = "on" ] && $SVDRPSEND "MESG $script: $template not found"
+ exit 7
+ fi
+
+ if [ "$WATERMARK" = "on" ]; then
+ convert -font helvetica -fill black -pointsize 32 -draw "text 250,460 '$WMTEXT'" "$template" "$tmpdir/template.png"
+ template="$tmpdir/template.png"
+ fi
+
+ case $1 in
+ atop)
+ composite -gravity center -compose atop -quality 100 "$template" "$tmpdir/$image" "$tmpdir/tmp_$image"
+ ;;
+ blend)
+ image_s=$(identify -format "%wx%h" "$template")
+ composite -gravity center -blend $image_s -quality 100 "$template" "$tmpdir/$image" "$tmpdir/tmp_$image"
+ ;;
+ esac
+ cp -f "$tmpdir/tmp_$image" "$tmpdir/$image"
+}
+
+function f_crop() {
+ array=($(identify -format "%w %h" "$tmpdir/$image"))
+ image_w=${array[0]}
+ image_h=${array[1]}
+ crop_w=$(($image_w / 36))
+ crop_h=$(($image_h / 36))
+ new_w=$(($image_w - $crop_w * 2))
+ new_h=$(($image_h - $crop_h * 2))
+ new_s=$(echo "$new_w x $new_h" | sed -e 's/ //g')
+ mogrify -crop $new_s+$crop_w+$crop_h "$tmpdir/$image"
+ mogrify -extent $new_s "$tmpdir/$image"
+}
+
+function f_depth() {
+ mogrify -depth 8 "$tmpdir/$image" # 8 or 16
+}
+
+function f_draw() {
+ template="$tmpdir/template.png"
+ # create template.png
+ case $1 in
+ bigsize)
+ convert -size 720x576 xc:transparent -fill "#000000" \
+ -stroke "#ffffff" -strokewidth 2 \
+ -draw 'rectangle 60,35 660,95' \
+ -draw 'rectangle 60,105 660,475' "$tmpdir/tmp.png"
+ tux_align="right"
+ ;;
+ normal)
+ convert -size 720x576 xc:transparent -fill "#000000" \
+ -draw 'roundRectangle 70,35 650,95 10,10' \
+ -draw 'roundRectangle 70,105 550,475 10,10' "$tmpdir/tmp.png"
+ ;;
+ storke)
+ convert -size 720x576 xc:transparent -fill "#000000" \
+ -stroke "#ffffff" -strokewidth 2 \
+ -draw 'roundRectangle 70,35 650,95 10,10' \
+ -draw 'roundRectangle 70,105 550,475 10,10' "$tmpdir/tmp.png"
+ ;;
+ user)
+ convert -size 720x576 xc:transparent -fill "#000000" \
+ -stroke "#303030" -strokewidth 5 \
+ -draw 'polygon 70,35 650,35 650,95 550,95 550,475 70,475' "$tmpdir/tmp.png"
+ ;;
+ esac
+
+ # reduce alphachannel about 50% and create the watermark
+ if [ "$WATERMARK" = "on" ]; then
+ convert -font helvetica -fill "#303030" -pointsize 32 -draw "text 250,460 '$WMTEXT'" -channel Alpha -fx 'u/2.0' "$tmpdir/tmp.png" "$template"
+ else
+ convert -channel Alpha -fx 'u/2.0' "$tmpdir/tmp.png" "$template"
+ fi
+ # composite
+ composite -gravity center -compose atop -quality 100 "$template" "$tmpdir/$image" "$tmpdir/tmp_$image"
+ cp -f "$tmpdir/tmp_$image" "$tmpdir/$image"
+}
+
+function f_resize() {
+ image_h=$(identify -format "%h" "$tmpdir/$image")
+ (($image_h != 576)) && mogrify -resize x576 "$tmpdir/$image"
+ image_w=$(identify -format "%w" "$tmpdir/$image")
+ if (($image_w > 720)); then
+ crop_w=$((($image_w - 720) / 2))
+ mogrify -crop 720x576+${crop_w}+0 "$tmpdir/$image"
+ elif (($image_w < 720)); then
+ mogrify -resize 720 "$tmpdir/$image"
+ image_h=$(identify -format "%h" "$tmpdir/$image")
+ crop_h=$((($image_h - 576) / 2))
+ (($image_h > 576)) && mogrify -crop 720x576+0+$crop_h "$tmpdir/$image"
+ fi
+ mogrify -extent 720x576 "$tmpdir/$image"
+}
+
+function f_serial_foto() {
+ aspect="4x3"
+ sec=1
+ images=""
+ x=130
+ while [ $sec -le 4 ]; do
+ sleep 5
+ $SVDRPSEND grab - 100 $WIDTH 576 | \
+ sed -n -e 's/^216-//p' -e '1ibegin-base64 644 -' -e '$a====' | \
+ uudecode -o "$tmpdir/serial-$sec.jpg"
+ BYTES=$(ls -l "$tmpdir/serial-$sec.jpg" | tr -s " " | cut -d " " -f 5)
+ if [ "$BYTES" = "0" ]; then
+ echo "$script: Snapshot is empty ..."
+ [ "$OSDMSG" = "on" ] && $SVDRPSEND "MESG $script: Snapshot is empty"
+ return 1
+ fi
+ [ "$WIDTH" = "768" ] && mogrify -crop 720x576+24+0 "$tmpdir/serial-$sec.jpg"
+
+ # autodetect blackbars
+ if [ "$aspect" != "16x9" ] && [ ! -f "$tmpdir/blackbar.jpg" ]; then
+ cp -f "$tmpdir/serial-$sec.jpg" "$tmpdir/blackbar.jpg"
+ mogrify -crop 720x5+0+566 "$tmpdir/blackbar.jpg"
+ colors=$(identify -verbose "$tmpdir/blackbar.jpg" | grep Type: | cut -d ":" -f2 | sed -e 's/ //g')
+ [ "$colors" = "Grayscale" ] && aspect="16x9"
+ fi
+ case $aspect in
+ 4x3)
+ mogrify -crop 680x544+20+16 "$tmpdir/serial-$sec.jpg"
+ ;;
+ 16x9)
+ mogrify -crop 680x382+20+97 "$tmpdir/serial-$sec.jpg"
+ ;;
+ esac
+
+ mogrify -resize 90x "$tmpdir/serial-$sec.jpg"
+ composite -compose atop -geometry +560+$x -quality 100 "$tmpdir/serial-$sec.jpg" "$tmpdir/$image" "$tmpdir/tmp_$image"
+ cp -f "$tmpdir/tmp_$image" "$tmpdir/$image"
+ sec=$(($sec+1))
+ ((x=$x+82))
+ done
+ tux_align="center"
+}
+
+function f_serial_movie() {
+ aspect="4x3"
+ sec=1
+ images=""
+ while [ $sec -le 5 ]; do
+ sleep 5
+ $SVDRPSEND grab - 100 $WIDTH 576 | \
+ sed -n -e 's/^216-//p' -e '1ibegin-base64 644 -' -e '$a====' | \
+ uudecode -o "$tmpdir/serial-$sec.jpg"
+ BYTES=$(ls -l "$tmpdir/serial-$sec.jpg" | tr -s " " | cut -d " " -f 5)
+ if [ "$BYTES" = "0" ]; then
+ echo "$script: Snapshot is empty ..."
+ [ "$OSDMSG" = "on" ] && $SVDRPSEND "MESG $script: Snapshot is empty"
+ return 1
+ fi
+ images="$images $tmpdir/serial-$sec.jpg"
+ [ "$WIDTH" = "768" ] && mogrify -crop 720x576+24+0 "$tmpdir/serial-$sec.jpg"
+# mogrify -crop 680x544+20+16 "$tmpdir/serial-$sec.jpg"
+ # autodetect blackbars
+ if [ "$aspect" != "16x9" ] && [ ! -f "$tmpdir/blackbar.jpg" ]; then
+ cp -f "$tmpdir/serial-$sec.jpg" "$tmpdir/blackbar.jpg"
+ mogrify -crop 720x5+0+566 "$tmpdir/blackbar.jpg"
+ colors=$(identify -verbose "$tmpdir/blackbar.jpg" | grep Type: | cut -d ":" -f2 | sed -e 's/ //g')
+ [ "$colors" = "Grayscale" ] && aspect="16x9"
+ fi
+ case $aspect in
+ 4x3)
+ mogrify -crop 680x544+20+16 "$tmpdir/serial-$sec.jpg"
+ ;;
+ 16x9)
+ mogrify -crop 680x382+20+97 "$tmpdir/serial-$sec.jpg"
+ ;;
+ esac
+ sec=$(($sec+1))
+ done
+ montage -background "#000000" -tile 5x1 -geometry +10+60 -rotate -90 $images "$tmpdir/montage.jpg"
+ mogrify -fill "#ffffff" \
+ -draw 'rectangle 10,10 50,50' \
+ -draw 'rectangle 70,10 110,50' \
+ -draw 'rectangle 130,10 170,50' \
+ -draw 'rectangle 190,10 230,50' \
+ -draw 'rectangle 250,10 290,50' \
+ -draw 'rectangle 310,10 350,50' \
+ -draw 'rectangle 370,10 410,50' \
+ -draw 'rectangle 430,10 470,50' \
+ -draw 'rectangle 490,10 530,50' \
+ -draw 'rectangle 550,10 590,50' \
+ -draw 'rectangle 610,10 650,50' \
+ -draw 'rectangle 670,10 710,50' \
+ -draw 'rectangle 730,10 770,50' \
+ -draw 'rectangle 790,10 830,50' \
+ -draw 'rectangle 850,10 890,50' \
+ -draw 'rectangle 910,10 950,50' \
+ -draw 'rectangle 970,10 1010,50' \
+ -draw 'rectangle 1030,10 1070,50' \
+ -draw 'rectangle 1090,10 1130,50' \
+ -draw 'rectangle 1150,10 1190,50' \
+ -draw 'rectangle 1210,10 1250,50' \
+ -draw 'rectangle 1270,10 1310,50' \
+ -draw 'rectangle 1330,10 1370,50' \
+ -draw 'rectangle 1390,10 1430,50' \
+ -draw 'rectangle 1450,10 1490,50' \
+ -draw 'rectangle 1510,10 1550,50' \
+ -draw 'rectangle 1570,10 1610,50' \
+ -draw 'rectangle 1630,10 1670,50' \
+ -draw 'rectangle 1690,10 1730,50' \
+ -draw 'rectangle 1750,10 1790,50' \
+ -draw 'rectangle 1810,10 1850,50' \
+ -draw 'rectangle 1870,10 1910,50' \
+ -draw 'rectangle 1930,10 1970,50' \
+ -draw 'rectangle 1990,10 2030,50' \
+ -draw 'rectangle 2050,10 2090,50' \
+ -draw 'rectangle 2110,10 2150,50' \
+ -draw 'rectangle 2170,10 2210,50' \
+ -draw 'rectangle 2230,10 2270,50' \
+ -draw 'rectangle 2290,10 2330,50' \
+ -draw 'rectangle 2350,10 2390,50' \
+ -draw 'rectangle 2410,10 2450,50' \
+ -draw 'rectangle 2470,10 2510,50' \
+ -draw 'rectangle 2530,10 2570,50' \
+ -draw 'rectangle 2590,10 2630,50' \
+ -draw 'rectangle 2650,10 2690,50' \
+ -draw 'rectangle 2710,10 2750,50' \
+ -draw 'rectangle 2770,10 2810,50' \
+ -draw 'rectangle 10,750 50,790' \
+ -draw 'rectangle 70,750 110,790' \
+ -draw 'rectangle 130,750 170,790' \
+ -draw 'rectangle 190,750 230,790' \
+ -draw 'rectangle 250,750 290,790' \
+ -draw 'rectangle 310,750 350,790' \
+ -draw 'rectangle 370,750 410,790' \
+ -draw 'rectangle 430,750 470,790' \
+ -draw 'rectangle 490,750 530,790' \
+ -draw 'rectangle 550,750 590,790' \
+ -draw 'rectangle 610,750 650,790' \
+ -draw 'rectangle 670,750 710,790' \
+ -draw 'rectangle 730,750 770,790' \
+ -draw 'rectangle 790,750 830,790' \
+ -draw 'rectangle 850,750 890,790' \
+ -draw 'rectangle 910,750 950,790' \
+ -draw 'rectangle 970,750 1010,790' \
+ -draw 'rectangle 1030,750 1070,790' \
+ -draw 'rectangle 1090,750 1130,790' \
+ -draw 'rectangle 1150,750 1190,790' \
+ -draw 'rectangle 1210,750 1250,790' \
+ -draw 'rectangle 1270,750 1310,790' \
+ -draw 'rectangle 1330,750 1370,790' \
+ -draw 'rectangle 1390,750 1430,790' \
+ -draw 'rectangle 1450,750 1490,790' \
+ -draw 'rectangle 1510,750 1550,790' \
+ -draw 'rectangle 1570,750 1610,790' \
+ -draw 'rectangle 1630,750 1670,790' \
+ -draw 'rectangle 1690,750 1730,790' \
+ -draw 'rectangle 1750,750 1790,790' \
+ -draw 'rectangle 1810,750 1850,790' \
+ -draw 'rectangle 1870,750 1910,790' \
+ -draw 'rectangle 1930,750 1970,790' \
+ -draw 'rectangle 1990,750 2030,790' \
+ -draw 'rectangle 2050,750 2090,790' \
+ -draw 'rectangle 2110,750 2150,790' \
+ -draw 'rectangle 2170,750 2210,790' \
+ -draw 'rectangle 2230,750 2270,790' \
+ -draw 'rectangle 2290,750 2330,790' \
+ -draw 'rectangle 2350,750 2390,790' \
+ -draw 'rectangle 2410,750 2450,790' \
+ -draw 'rectangle 2470,750 2510,790' \
+ -draw 'rectangle 2530,750 2570,790' \
+ -draw 'rectangle 2590,750 2630,790' \
+ -draw 'rectangle 2650,750 2690,790' \
+ -draw 'rectangle 2710,750 2750,790' \
+ -draw 'rectangle 2770,750 2810,790' \
+ "$tmpdir/montage.jpg"
+ mogrify -rotate 90 -resize 90x "$tmpdir/montage.jpg"
+ composite -compose atop -geometry +560+130 -quality 100 "$tmpdir/montage.jpg" "$tmpdir/$image" "$tmpdir/tmp_$image"
+ cp -f "$tmpdir/tmp_$image" "$tmpdir/$image"
+ tux_align="center"
+}
+
+function f_sharpen() {
+ mogrify -unsharp 0 "$tmpdir/$image"
+}
+
+function f_tux() {
+ # template
+ template="$targetdir/tux.png"
+ if [ ! -f "$template" ]; then
+ echo "$script: $template not found ..."
+ [ "$OSDMSG" = "on" ] && $SVDRPSEND "MESG $script: $template not found"
+ return 1
+ fi
+ array=($(identify -format "%w %h" "$template"))
+ image_w=${array[0]}
+ image_h=${array[1]}
+ new_w=$((550 - $image_w))
+ new_h=$((475 - $image_h))
+ [ "$tux_align" = "right" ] && new_w=$((660 - $image_w))
+ [ "$WATERMARK" = "on" ] && new_h=$((440 - $image_w))
+ tux_align="center"
+ convert -channel Alpha -fx 'u/4.0' "$template" "$tmpdir/tmp.png"
+ composite -compose atop -geometry +$new_w+$new_h -quality 100 "$tmpdir/tmp.png" "$tmpdir/$image" "$tmpdir/tmp_$image"
+ cp -f "$tmpdir/tmp_$image" "$tmpdir/$image"
+}
+
+function f_zoom_center() {
+ array=($(identify -format "%w %h" "$tmpdir/$image"))
+ image_w=${array[0]}
+ image_h=${array[1]}
+ new_w=$(($image_w * 3 / 4))
+ new_h=$(($image_h * 3 / 4))
+ crop_w=$((($image_w - $new_w) / 2))
+ crop_h=$((($image_h - $new_h) / 2))
+ new_s=$(echo "$new_w x $new_h" | sed -e 's/ //g')
+ mogrify -crop $new_s+$crop_w+$crop_h "$tmpdir/$image"
+ mogrify -extent $new_s "$tmpdir/$image"
+}
+
+function f_zoom_left() {
+ array=($(identify -format "%w %h" "$tmpdir/$image"))
+ image_w=${array[0]}
+ image_h=${array[1]}
+ new_w=$(($image_w * 3 / 4))
+ new_h=$(($image_h * 3 / 4))
+ crop_w=0
+ crop_h=$((($image_h - $new_h) / 2))
+ new_s=$(echo "$new_w x $new_h" | sed -e 's/ //g')
+ mogrify -crop $new_s+$crop_w+$crop_h "$tmpdir/$image"
+ mogrify -extent $new_s "$tmpdir/$image"
+}
+
+function f_zoom_right() {
+ array=($(identify -format "%w %h" "$tmpdir/$image"))
+ image_w=${array[0]}
+ image_h=${array[1]}
+ new_w=$(($image_w * 3 / 4))
+ new_h=$(($image_h * 3 / 4))
+ crop_w=$(($image_w - $new_w))
+ crop_h=$((($image_h - $new_h) / 2))
+ new_s=$(echo "$new_w x $new_h" | sed -e 's/ //g')
+ mogrify -crop $new_s+$crop_w+$crop_h "$tmpdir/$image"
+ mogrify -extent $new_s "$tmpdir/$image"
+}
+
+function f_zoom_center_16x9() {
+ array=($(identify -format "%wx%h %w %h" "$tmpdir/$image"))
+ image_s=${array[0]}
+ image_w=${array[1]}
+ image_h=${array[2]}
+ new_w=$(($image_h * 16 / 9))
+ crop_w=$((($new_w - $image_w) / 2))
+ new_s=$(echo "$new_w x $image_h" | sed -e 's/ //g')
+ mogrify -resize $new_s\! -crop $image_s+$crop_w+0 "$tmpdir/$image"
+ mogrify -extent $image_s "$tmpdir/$image"
+}
+
+function f_zoom_left_16x9() {
+ array=($(identify -format "%wx%h %w %h" "$tmpdir/$image"))
+ image_s=${array[0]}
+ image_w=${array[1]}
+ image_h=${array[2]}
+ new_w=$(($image_h * 16 / 9))
+ crop_w=0
+ new_s=$(echo "$new_w x $image_h" | sed -e 's/ //g')
+ mogrify -resize $new_s\! -crop $image_s+$crop_w+0 "$tmpdir/$image"
+ mogrify -extent $image_s "$tmpdir/$image"
+}
+
+function f_zoom_right_16x9() {
+ array=($(identify -format "%wx%h %w %h" "$tmpdir/$image"))
+ image_s=${array[0]}
+ image_w=${array[1]}
+ image_h=${array[2]}
+ new_w=$(($image_h * 16 / 9))
+ crop_w=$(($new_w - $image_w))
+ new_s=$(echo "$new_w x $image_h" | sed -e 's/ //g')
+ mogrify -resize $new_s\! -crop $image_s+$crop_w+0 "$tmpdir/$image"
+ mogrify -extent $image_s "$tmpdir/$image"
+}
+
+function f_letterbox_16x9() {
+ array=($(identify -format "%wx%h %w %h" "$tmpdir/$image"))
+ image_s=${array[0]}
+ image_w=${array[1]}
+ image_h=${array[2]}
+ new_w=$(($image_h * 16 / 9))
+ new_h=$(($new_w * 4 / 5))
+ (( new_h == 819 )) && new_h=820
+ bar=$((($new_h - $image_h) / 2))
+ new_h1=$(($image_h + $bar))
+ new_h2=$(($image_h + $bar + $bar))
+ new_s1=$(echo "$image_w x $new_h1" | sed -e 's/ //g')
+ new_s2=$(echo "$image_w x $new_h2" | sed -e 's/ //g')
+ mogrify -rotate 180 -extent $new_s1 -rotate 180 -extent $new_s2 -resize $image_s\! "$tmpdir/$image"
+}
+
+function filter_select() {
+ for f in $FILTER; do
+ case $f in
+ comp-atop)
+ f_comp atop
+ ;;
+ comp-blend)
+ f_comp blend
+ ;;
+ crop)
+ f_crop
+ ;;
+ depth)
+ f_depth
+ ;;
+ draw-bigsize)
+ f_draw bigsize
+ ;;
+ draw-normal)
+ f_draw normal
+ ;;
+ draw-storke)
+ f_draw storke
+ ;;
+ draw-user)
+ f_draw user
+ ;;
+ resize)
+ f_resize
+ ;;
+ serial-foto)
+ f_serial_foto
+ ;;
+ serial-movie)
+ f_serial_movie
+ ;;
+ sharpen)
+ f_sharpen
+ ;;
+ tux)
+ f_tux right
+ ;;
+ zoom-center)
+ f_zoom_center
+ ;;
+ zoom-left)
+ f_zoom_left
+ ;;
+ zoom-right)
+ f_zoom_right
+ ;;
+ zoom-center-16x9)
+ f_zoom_center_16x9
+ ;;
+ zoom-left-16x9)
+ f_zoom_left_16x9
+ ;;
+ zoom-right-16x9)
+ f_zoom_right_16x9
+ ;;
+ letterbox-16x9)
+ f_letterbox_16x9
+ ;;
+ esac
+ done
+}
+
+function final_action() {
+ # install the new image
+ image_name=$(echo "$image" | cut -f1 -d "." - )
+ convert -comment "$version" "$tmpdir/$image" "$targetdir/$script/$image_name.png"
+ ln -fs "$targetdir/$script/$image_name.png" "$TARGET"
+
+ # clean up
+ rm -rf "$tmpdir"
+
+ # messages
+ [ "$OSDMSG" = "on" ] && $SVDRPSEND "MESG $script: Image created"
+
+ if [ "$INFO" = "on" ]; then
+ echo "$script: $(date '+%F %T')"
+ echo "image: $IMAGE"
+ echo "filter: $FILTER"
+ echo -n "target:"; identify -format "%f %wx%h" "$targetdir/$script/$image_name.png"
+ echo "template: $template"
+ echo "tmpdir: $tmpdir"
+ fi
+
+ [ "$SHOWFILE" = "on" ] && display "$targetdir/$script/$image_name.png" &
+}
+
+## PROG
+# arguments
+for i in $args; do
+ case $1 in
+ -showfile|-x)
+ shift 1
+ SHOWFILE="$1"
+ [ "$SHOWFILE" != "on" ] && SHOWFILE="off"
+ ;;
+ -filter|-f)
+ shift 1
+ FILTER="$FILTER $1"
+ ;;
+ -image|-i)
+ shift 1
+ IMAGE="$1"
+ ;;
+ -target|-t)
+ shift 1
+ TARGET="$1"
+ ;;
+ -template|-T)
+ shift 1
+ TEMPLATE="$1"
+ ;;
+ -osd|-o)
+ shift 1
+ OSDMSG="$1"
+ [ "$OSDMSG" != "on" ] && OSDMSG="off"
+ ;;
+ -watermark|-w)
+ shift 1
+ [ "$1" != "default" ] && WMTEXT="$1"
+ WATERMARK="on"
+ ;;
+ -width|-W)
+ shift 1
+ if [ "$1" = "768" ]; then
+ WIDTH="768"
+ else
+ WIDTH="720"
+ fi
+ ;;
+ -dialog|-d)
+ use_dialog="true"
+ ;;
+ -help|-h)
+ echo "$script ($version)"
+ echo "use: $script [-argument value]"
+ echo "optional arguments are:"
+ echo "-f | -filter crop"
+ echo "-f | -filter zoom-center|zoom-left|zoom-right|zoom-center-16x9|zoom-left-16x9|zoom-right-16x9|letterbox-16x9"
+ echo "-f | -filter resize"
+ echo "-f | -filter comp-atop|comp-blend|draw-bigsize|draw-normal|draw-storke|draw-user"
+ echo "-f | -filter serial-foto|serial-movie"
+ echo "-f | -filter tux"
+ echo "-f | -filter unsharp"
+ echo "-f | -filter depth"
+ echo "-i | -image /path/to/image.ext"
+ echo "-o | -osd on|off"
+ echo "-t | -target /path/to/plugins/burn/menu-bg.png"
+ echo "-T | -template /path/to/template.png"
+ echo "-w | -watermark ?|default|\"some text\""
+ echo "-W | -width 768|720"
+ echo "-x | -showfile on|off"
+ echo "-d | -dialog"
+ echo "-h | -help"
+ echo "-v | -version"
+ echo ""
+ exit 0
+ ;;
+ -version|-v)
+ echo "$script ($version)"
+ echo ""
+ exit 0
+ ;;
+ esac
+ shift 1
+done
+
+# MAIN PROG
+if [ ! "$args" ] || [ "$use_dialog" = "true" ]; then # DIALOG
+ # filter defaults
+ crop="on"
+ zoomcenter="off"
+ zoomleft="off"
+ zoomright="off"
+ resize="on"
+ drawbigsize="off"
+ drawnormal="off"
+ drawstorke="off"
+ drawuser="off"
+ serialfoto="off"
+ serialmovie="off"
+ tux="off"
+ sharpen="off"
+ depth="off"
+ SHOWFILE="on"
+ OSDMSG="off"
+
+ #WATERMARK="on"
+ # watermark text input
+ if [ "$WMTEXT" = "?" ]; then
+ WMTEXT_DEF=" powered by VDR"
+ case $language in
+ deu) menutext="Text fuer das Wasserzeichen" ;;
+ eng) menutext="Text for the watermark" ;;
+ fra) menutext="Watermark" ;;
+ esac
+ TMP_WMTEXT=`Xdialog --left --wrap --title " $version " --inputbox "$menutext" 0 0 "$WMTEXT_DEF" 3>&1 1>&2 2>&3`
+ if [ $? = 0 ]; then
+ WMTEXT="$TMP_WMTEXT"
+ else
+ WMTEXT="$WMTEXT_DEF"
+ fi
+ fi
+
+ # language
+ case $language in
+ deu)
+ # menutext
+ menutext="Von den Filtern mit zoom-*, draw-* oder serial-*\nsollte jeweils nur einer ausgewaehlt werden."
+ # helptext
+ text_crop="Raender abschneiden"
+ text_zoomcenter="Zoom auf die Mitte des Bildes"
+ text_zoomleft="Zoom auf die linke Seite des Bildes"
+ text_zoomright="Zoom auf die rechte Seite des Bildes"
+ text_resize="Skaliert das bearbeitete Bild wieder auf 720x576. Dieser Filter muss aktiviert werden, wenn crop oder zoom* benutzt werden"
+ text_drawbigsize="Zeichnet zwei grosse, transparente Textfelder mit Rahmen ueber das Bild"
+ text_drawnormal="Zeichnet zwei transparente Textfelder mit runde Ecken ueber das Bild"
+ text_drawstorke="Zeichnet zwei transparente Textfelder mit runde Ecken und Rahmen ueber das Bild"
+ text_drawuser="Zeichnet ueber das Bild"
+ text_watermark="Blendet ein Wasserzeichen in das Textfeld. Text: \"$WMTEXT\""
+ text_serialfoto="Legt eine Serie von vier verkleinerten Screenshots ueber das Bild"
+ text_serialmovie="Montiert eine Serie von fuenf Screenshots zu einem Filmstreifen und legt diesen ueber das Bild"
+ text_tux="Blendet einen Tux in das Bild"
+ text_sharpen="Leichtes anheben der Bildschaerfe"
+ text_depth="Reduziert die Farbtiefe auf 24bpp (damit das Bild mit dem Image-Plugin angezeigt werden kann)"
+ text_showfile="Fertiges Bild anzeigen"
+ ;;
+ eng)
+ ;;
+ fra)
+ ;;
+ esac
+
+ while true; do
+ options=`Xdialog --item-help --no-tags --left --wrap --separate-output --cancel-label "Exit" --title " $version " \
+ --checklist "$menutext" 0 0 17 \
+ "1" "crop" "$crop" "$text_crop" \
+ "2" "zoom-center" "$zoomcenter" "$text_zoomcenter" \
+ "3" "zoom-left" "$zoomleft" "$text_zoomleft" \
+ "4" "zoom-right" "$zoomright" "$text_zoomright" \
+ "5" "resize" "$resize" "$text_resize" \
+ "6" "draw-bigsize" "$drawbigsize" "$text_drawbigsize" \
+ "7" "draw-normal" "$drawnormal" "$text_drawnormal" \
+ "8" "draw-storke" "$drawstorke" "$text_drawstorke" \
+ "9" "draw-user" "$drawuser" "$text_drawuser" \
+ "10" "watermark" "$WATERMARK" "$text_watermark" \
+ "11" "serial-foto" "$serialfoto" "$text_serialfoto" \
+ "12" "serial-movie" "$serialmovie" "$text_serialmovie" \
+ "13" "tux" "$tux" "$text_tux" \
+ "14" "sharpen" "$sharpen" "$text_sharpen" \
+ "15" "depth" "$depth" "$text_depth" \
+ "16" "showfile" "$SHOWFILE" "$text_showfile" 3>&1 1>&2 2>&3`
+ [ $? != 0 ] && exit 0
+
+ # settings
+ FILTER=""
+ crop="off"
+ zoomcenter="off"
+ zoomleft="off"
+ zoomright="off"
+ resize="off"
+ drawbigsize="off"
+ drawnormal="off"
+ drawstorke="off"
+ drawuser="off"
+ serialfoto="off"
+ serialmovie="off"
+ tux="off"
+ sharpen="off"
+ depth="off"
+ SHOWFILE="off"
+ WATERMARK="off"
+
+ for i in $options; do
+ case "$i" in
+ 1) crop="on"; FILTER="$FILTER crop" ;;
+ 2) zoomcenter="on"; FILTER="$FILTER zoom-center" ;;
+ 3) zoomleft="on"; FILTER="$FILTER zoom-left" ;;
+ 4) zoomright="on"; FILTER="$FILTER zoom-right" ;;
+ 5) resize="on"; FILTER="$FILTER resize" ;;
+ 6) drawbigsize="on"; FILTER="$FILTER draw-bigsize" ;;
+ 7) drawnormal="on"; FILTER="$FILTER draw-normal" ;;
+ 8) drawstorke="on"; FILTER="$FILTER draw-storke" ;;
+ 9) drawuser="on"; FILTER="$FILTER draw-user" ;;
+ 10) WATERMARK="on" ;;
+ 11) serialfoto="on"; FILTER="$FILTER serial-foto" ;;
+ 12) serialmovie="on"; FILTER="$FILTER serial-movie" ;;
+ 13) tux="on"; FILTER="$FILTER tux" ;;
+ 14) sharpen="on"; FILTER="$FILTER sharpen" ;;
+ 15) depth="on"; FILTER="$FILTER depth" ;;
+ 16) SHOWFILE="on" ;;
+ esac
+ done
+ # action
+ start_action
+ # apply filter
+ filter_select
+ # final
+ final_action
+ done
+else # MAIN PROG
+ # action
+ start_action
+ # apply filter
+ filter_select
+ # final
+ final_action
+fi
+
+exit 0
diff --git a/vdr/scripts/runvdr b/vdr/scripts/runvdr
new file mode 100644
index 0000000..36115ea
--- /dev/null
+++ b/vdr/scripts/runvdr
@@ -0,0 +1,247 @@
+#!/bin/sh
+
+# x-vdr (Installations-Skript fuer einen VDR mit Debian als Basis)
+# von Marc Wernecke - www.zulu-entertainment.de
+# 24.03.2009
+# runvdr
+
+VDRLANG="de_DE.iso88591"
+VDRCHARSET="ISO-8859-9"
+KILL="/usr/bin/killall -q -TERM"
+KERNEL=`uname -r`
+VDRCONFIG=/etc/default/vdr
+LOGGER="logger -s ${0##*/} "
+
+function VdrSettings() {
+ # read vdr default configuration
+ if [ -f "$VDRCONFIG" ]; then
+ . $VDRCONFIG
+ else
+ $LOGGER "exit, because $VDRCONFIG does not exists"
+ exit 1
+ fi
+
+ # read sysconfig generated by setup-plugin
+ if [ "$setup" = "on" ] && [ -f "$VDRCONFDIR/plugins/setup/sysconfig" ]; then
+ . "$VDRCONFDIR/plugins/setup/sysconfig"
+ fi
+
+ # test if vdr exists and is executable
+ test -x "$VDRPRG" || exit 0
+
+ # create tmp files
+ $VDRPRG --help | sed -e '/^$/d' -e 's/^. //' > /tmp/vdr-help.tmp
+ $VDRPRG --version | sed -e 's/\(^[^ ]*\)\(.*$\)/\1_desc="\1\2"/' -e '/^streamdev/s/-/_/' -e 's/^\[.*//' | sort > /tmp/vdr-desc.cache
+
+ # export vdr language
+ export LANG=$VDRLANG
+ export LC_ALL=$VDRLANG
+ export VDR_CHARSET_OVERRIDE=$VDRCHARSET
+
+ # export HOME
+ if [ "$VDRUSER" = "root" ]; then
+ export HOME="/root"
+ else
+ export HOME="$VDRCONFDIR"
+ fi
+
+ # java / projectx for burn and dvdconvert
+ if [ "$burn" = "on" ] || [ "$dvdconvert" = "on" ]; then
+ [ -z "$JAVA_HOME" ] && export JAVA_HOME="/usr/lib/jvm/java-6-sun/jre"
+ [ -z "$PROJECTX_HOME" ] && export PROJECTX_HOME="/usr/bin/ProjectX"
+ fi
+
+ # set DISPLAY
+ # - activate automatic login
+ # - copy x-vdr/extra/xhost to ~/.kde/Autostart and set xhost executable
+ if [ "$softdevice" = "on" ] || [ "$xineliboutput" = "on" ]; then
+ [ -z "$DISPLAY" ] && export DISPLAY=:$XV_DISPLAY
+ fi
+
+ # load nvram
+ if [ `which nvram-wakeup | wc -l` -gt 0 ] && [ `lsmod | grep -cw 'nvram'` -eq 0 ]; then
+ modprobe -q nvram
+ fi
+
+ # load cdfs
+ if [ `lsmod | grep -cw 'cdfs'` -eq 0 ]; then
+ modprobe -q cdfs
+ fi
+
+ # load av7710ir
+ if [ -e /proc/av7110_ir ] || [ -e /proc/budget_ci_ir ]; then
+ for FILE in av7110_ir budget_ci_ir; do
+ if [ -e "/proc/$FILE" ]; then
+ chown $VDRUSER:$VDRGROUP /proc/$FILE
+ fi
+ done
+ if [ `lsmod | grep -cw 'evdev'` -eq 0 ]; then modprobe -q evdev ; fi
+ fi
+
+ # load em8300
+ if [ "$em8300" = "on" ]; then
+# EM8300="/lib/modules/$KERNEL/em8300"
+# if [ -f "$EM8300/adv717x.ko" ] && [ -f "$EM8300/bt865.ko" ] && [ -f "$EM8300/em8300.ko" ]; then
+# $VDRSCRIPTDIR/rmm
+# $VDRSCRIPTDIR/ldm
+ [ "$dxr3" != "on" ] && em8300setup
+# fi
+ fi
+
+ # load parport
+ if [ "$graphlcd_base" = "on" ]; then
+ [ `lsmod | grep -cw 'parport'` -eq 0 ] && modprobe -q parport
+ [ `lsmod | grep -cw 'parport_pc'` -eq 0 ] && modprobe -q parport_pc
+ [ `lsmod | grep -cw 'ppdev'` -eq 0 ] && modprobe -q ppdev
+# chmod 0666 /dev/parport0
+ fi
+
+ # only for mplayer via xine
+ # - copy x-vdr/extra/xhost to ~/.kde/Autostart and set xhost executable
+ if [ "$VDRUSER" != "root" ] && [ "$xine" = "on" ] && [ "$mplayer" = "on" ]; then
+ chmod 666 /dev/rtc
+# echo 1024 > /proc/sys/dev/rtc/max-user-freq
+ fi
+
+ # plugins
+ PLUGINS=""
+ ignore_cmd=""
+
+ if [ "$USE_SYSCONFIG" = "on" ]; then # use PLUGINLIST from sysconfig
+ PLUGINS="$PLUGINLIST"
+
+ else # start creating a temporary pluginlist ...
+ # plugin api version
+ plugin_api="$(grep -m1 '^vdr_desc=' /tmp/vdr-desc.cache | sed -e 's/^vdr_desc=//' -e 's/[^)]*$//' -e 's/^[^\/]*//g' -e 's/\///' -e 's/\/*)//')"
+ [ -n "$plugin_api" ] || plugin_api="$($VDRPRG --version | grep -m1 '^vdr ' | sed -e 's/[^)]*$//' -e 's/^[^\/]*//g' -e 's/\///' -e 's/\/*)//')"
+
+ # put these plugins on top of the pluginlist
+ if [ -n "$PLUGINS_ON_TOP" ]; then
+ for p in $PLUGINS_ON_TOP ; do
+ if [ -f "$VDRLIBDIR/libvdr-${p}.so.${plugin_api}" ]; then
+ ignore_cmd="$ignore_cmd -I libvdr-${p}.so.${plugin_api}"
+ plugin=$(echo $p | sed -e "s/-/_/g") # this is for streamdev-...
+ if [ "${!plugin}" = "on" ]; then
+ plugin_args="${plugin}_args"
+ if [ -n "${!plugin_args}" ]; then
+ PLUGINS="$PLUGINS -P'${p} ${!plugin_args}'"
+ else
+ PLUGINS="$PLUGINS -P'${p}'"
+ fi
+ fi
+ fi
+ done
+ fi
+
+ # read libdir and continue creating pluginlist
+ files=$(ls -B $ignore_cmd "$VDRLIBDIR" | grep "^libvdr-" | grep ".so.${plugin_api}$" | sed -e "s/^libvdr-//g" -e "s/.so.${plugin_api}$//g")
+ if [ -n "$files" ]; then
+ for p in $files ; do
+ plugin=$(echo $p | sed -e "s/-/_/g") # this is for streamdev-...
+ if [ -z "${!plugin}" ] && [ -f "$VDRLIBDIR/libvdr-$(echo $plugin | sed -e "s/_/-/g").so.${plugin_api}" ]; then
+ echo "" >> $VDRCONFIG
+ echo "# $p" >> $VDRCONFIG
+ echo "${plugin}=\"off"\" >> $VDRCONFIG
+ eval ${plugin}="off"
+ # plugin args
+ nl=0; (( nl=$(grep -n "^$p " /tmp/vdr-help.tmp | cut -f 1 -d ":") +1 ))
+ [ $nl -gt 1 ] && [ $(sed -n "${nl}p" /tmp/vdr-help.tmp | grep -c "^-") -gt 0 ] && echo "${plugin}_args=\"\"" >> $VDRCONFIG
+ fi
+ if [ "${!plugin}" = "on" ]; then
+ plugin_args="${plugin}_args"
+ if [ -n "${!plugin_args}" ]; then
+ PLUGINS="$PLUGINS -P'${p} ${!plugin_args}'"
+ else
+ PLUGINS="$PLUGINS -P'${p}'"
+ fi
+ fi
+ done
+ fi
+
+ # special plugin settings
+ [ "$remote" = "on" ] && chmod 0666 $remote_event
+ fi
+
+ # scripts
+ VDRRECCMDS=""
+ [ -x "$VDRSCRIPTDIR/vdrreccmds" ] && VDRRECCMDS="-r $VDRSCRIPTDIR/vdrreccmds"
+ VDRSETTIME=""
+ [ -x "$VDRSCRIPTDIR/vdrsettime" ] && [ $(grep -c "\-T CMD, --timeset=CMD" /tmp/vdr-help.tmp) -gt 0 ] && VDRSETTIME="-T $VDRSCRIPTDIR/vdrsettime"
+ VDRSHUTDOWN=""
+ [ -x "$VDRSCRIPTDIR/vdrshutdown" ] && VDRSHUTDOWN="-s $VDRSCRIPTDIR/vdrshutdown"
+
+ # keyboard tty (terminal for vdr)
+ VDRKEYBTTY=""
+ if [ "$KEYB_TTY" != "" ]; then
+ VDRKEYBTTY="-t $KEYB_TTY"
+ [ "$VDRUSER" != "root" ] && chmod 0666 $KEYB_TTY
+ fi
+
+ # cmd
+ VDRCMD="$VDRPRG -w $WATCHDOG -v $VIDEODIR -c $VDRCONFDIR -E $VDRVARDIR/epg.data -L $VDRLIBDIR -u $VDRUSER \
+ $VDRRECCMDS $VDRSETTIME $VDRSHUTDOWN $VDRKEYBTTY $PLUGINS $OPTIONS "
+}
+
+# modules
+function get_modulenames() {
+ KVERS=`uname -r | grep -e '2.6'`
+ if [ -z "$KVERS" ]; then
+ MODULES=`lsmod | grep 'dvb-core' | cut -d'[' -f2 | cut -d']' -f1`
+ else
+ MODULES=`lsmod | grep '^dvb_core' | awk '{print $4;}' | awk '{ gsub(/,/," ", $1); print }'`
+ fi
+}
+
+get_modulenames
+
+if [ -z "$MODULES" ]; then # If no DVB-Modules were found, try to load
+ modprobe -q dvb # the module with the name dvb, this could
+ get_modulenames # be an alias for the dvb-ttpci-module or
+fi # another dvb-module
+
+MODULES="$MODULES dvb-core"
+
+if [ `echo $MODULES | grep -cw 'b2c2_flexcop'` -gt 0 ] && [ `echo $MODULES | grep -cw 'b2c2_flexcop_pci'` -eq 0 ]; then
+ MODULES="b2c2_flexcop_pci $MODULES"
+fi
+
+# clear TTY
+if [ "$KEYB_TTY" != "" ]; then
+ clear > $KEYB_TTY
+fi
+
+# main script
+while (true) do
+ # load vdr settings
+ VdrSettings
+ # start X
+ MSG="Starting Xserver for VDRUSER: '$VDRUSER'"
+ [ "$XPLAYER" = "on" ] && echo "$MSG" && su "$VDRUSER" -c "startx &"
+ # start vdr
+ $LOGGER "Starting Video Disk Recorder at `date`"
+ eval $VDRCMD >/dev/null 2>&1
+ if test $? -eq 0; then
+ $LOGGER "Video Disk Recorder stopped at `date`"
+ [ "$XPLAYER" = "on" ] && $KILL Xorg
+ exit 0
+ fi
+ # stop vdr
+ $LOGGER "Stopping Video Disk Recorder at `date`"
+ [ "$XPLAYER" = "on" ] && $KILL Xorg
+ $KILL $VDRPRG
+ wait `pidof $VDRPRG`
+ # unload modules
+ for MODULE in $MODULES; do
+ rmmod $MODULE >/dev/null 2>&1
+ wait `pidof rmmod`
+ done
+ # load modules
+ for MODULE in $MODULES; do
+ modprobe $MODULE >/dev/null 2>&1
+ done
+done
+
+# clear tty
+if [ "$KEYB_TTY" != "" ]; then
+ clear > $KEYB_TTY
+fi
diff --git a/vdr/scripts/runvdr-old b/vdr/scripts/runvdr-old
new file mode 100644
index 0000000..6e8373a
--- /dev/null
+++ b/vdr/scripts/runvdr-old
@@ -0,0 +1,319 @@
+#!/bin/sh
+
+# x-vdr (Installations-Skript fuer einen VDR mit Debian als Basis)
+# von Marc Wernecke - www.zulu-entertainment.de
+# 06.11.2007
+# runvdr
+
+#export LD_ASSUME_KERNEL=2.4.1
+export LANG=de_DE.iso8859-1
+export LC_ALL=de_DE.iso8859-1
+
+KILL="/usr/bin/killall -q -TERM"
+KERNEL=`uname -r`
+
+function vdr_settings() {
+ # load vdr configuration
+ if [ -f /etc/default/vdr ]; then
+ . /etc/default/vdr
+ else
+ logger "$0 - error -> /etc/default/vdr does not exists"
+ exit 1
+ fi
+
+ # test if vdr exists and is executable
+ test -x "$VDRPRG" || exit 0
+
+ # export HOME
+ if [ "$VDRUSER" = "root" ]; then
+ export HOME="/root"
+ else
+ export HOME="$VDRCONFDIR"
+ fi
+
+ # java for burn
+ if [ "$burn" = "on" ]; then
+ [ -z "$JAVA_HOME" ] && export JAVA_HOME="/usr/lib/jvm/java-1.5.0-sun/jre"
+ [ -z "$PROJECTX_HOME" ] && export PROJECTX_HOME="/usr/local/src/ProjectX"
+ fi
+
+ # set DISPLAY
+ # - activate automatic login
+ # - copy x-vdr/extra/xhost to ~/.kde/Autostart and set xhost executable
+ if [ "$softdevice" = "on" ] || [ "$xineliboutput" = "on" ]; then
+ [ -z "$DISPLAY" ] && export DISPLAY=:$XV_DISPLAY
+ fi
+
+ # load nvram
+ if [ `which nvram-wakeup | wc -l` -gt 0 ] && [ `lsmod | grep -cw 'nvram'` -eq 0 ]; then
+ modprobe -q nvram
+ fi
+
+ # load cdfs
+ if [ `lsmod | grep -cw 'cdfs'` -eq 0 ]; then
+ modprobe -q cdfs
+ fi
+
+ # load av7710ir
+ if [ -e /proc/av7110_ir ] || [ -e /proc/budget_ci_ir ]; then
+ for FILE in av7110_ir budget_ci_ir; do
+ if [ -e "/proc/$FILE" ]; then
+ chown $VDRUSER:$VDRGROUP /proc/$FILE
+ fi
+ done
+ if [ `lsmod | grep -cw 'evdev'` -eq 0 ]; then modprobe -q evdev ; fi
+ fi
+
+ # load em8300
+ if [ "$em8300" = "on" ]; then
+# EM8300="/lib/modules/$KERNEL/em8300"
+# if [ -f "$EM8300/adv717x.ko" ] && [ -f "$EM8300/bt865.ko" ] && [ -f "$EM8300/em8300.ko" ]; then
+# $VDRSCRIPTDIR/rmm
+# $VDRSCRIPTDIR/ldm
+ [ "$dxr3" != "on" ] && em8300setup
+# fi
+ fi
+
+ # load parport
+ if [ "$graphlcd_base" = "on" ]; then
+ [ `lsmod | grep -cw 'parport'` -eq 0 ] && modprobe -q parport
+ [ `lsmod | grep -cw 'parport_pc'` -eq 0 ] && modprobe -q parport_pc
+ [ `lsmod | grep -cw 'ppdev'` -eq 0 ] && modprobe -q ppdev
+ # chmod 0666 /dev/parport0
+ fi
+
+ # only for mplayer via xine
+ # - copy x-vdr/extra/xhost to ~/.kde/Autostart and set xhost executable
+ if [ "$VDRUSER" != "root" ] && [ "$xine" = "on" ] && [ "$mplayer" = "on" ]; then
+ chmod 666 /dev/rtc
+ echo 1024 > /proc/sys/dev/rtc/max-user-freq
+ fi
+
+
+ # plugins
+ PLUGINS=""
+ # video devices
+ [ "$dxr3" = "on" ] && PLUGINS="$PLUGINS -P'dxr3'"
+ [ "$softdevice" = "on" ] && PLUGINS="$PLUGINS -P'softdevice $softdevice_args'"
+ [ "$vdrtvtime" = "on" ] && PLUGINS="$PLUGINS -P'vdrtvtime $vdrtvtime_args'"
+ [ "$xine" = "on" ] && PLUGINS="$PLUGINS -P'xine $xine_args'"
+ [ "$xineliboutput" = "on" ] && PLUGINS="$PLUGINS -P'xineliboutput $xineliboutput_args'"
+ # other
+ [ "$admin" = "on" ] && PLUGINS="$PLUGINS -P'admin'"
+ [ "$adzap" = "on" ] && PLUGINS="$PLUGINS -P'adzap $adzap_args'"
+ [ "$alcd" = "on" ] && PLUGINS="$PLUGINS -P'alcd $alcd_args'"
+ [ "$analogradio" = "on" ] && PLUGINS="$PLUGINS -P'analogradio $analogradio_args'"
+ [ "$arghdirector" = "on" ] && PLUGINS="$PLUGINS -P'arghdirector'"
+ [ "$atmo" = "on" ] && PLUGINS="$PLUGINS -P'atmo $atmo_args'"
+ [ "$audiorecorder" = "on" ] && PLUGINS="$PLUGINS -P'audiorecorder $audiorecorder_args'"
+ [ "$autosort" = "on" ] && PLUGINS="$PLUGINS -P'autosort'"
+ [ "$autotimeredit" = "on" ] && PLUGINS="$PLUGINS -P'autotimeredit $autotimeredit_args'"
+ [ "$avards" = "on" ] && PLUGINS="$PLUGINS -P'avards $avards_args'"
+ [ "$avolctl" = "on" ] && PLUGINS="$PLUGINS -P'avolctl'"
+ [ "$beep" = "on" ] && PLUGINS="$PLUGINS -P'beep'"
+ [ "$bitstreamout" = "on" ] && PLUGINS="$PLUGINS -P'bitstreamout $bitstreamout_args'"
+ [ "$burn" = "on" ] && PLUGINS="$PLUGINS -P'burn $burn_args'"
+ [ "$calc" = "on" ] && PLUGINS="$PLUGINS -P'calc'"
+ [ "$cdda" = "on" ] && PLUGINS="$PLUGINS -P'cdda $cdda_args'"
+ [ "$chanorg" = "on" ] && PLUGINS="$PLUGINS -P'chanorg $chanorg_args'"
+ [ "$channelswitcher" = "on" ] && PLUGINS="$PLUGINS -P'channelswitcher'"
+ [ "$cinebars" = "on" ] && PLUGINS="$PLUGINS -P'cinebars'"
+ [ "$clock" = "on" ] && PLUGINS="$PLUGINS -P'clock'"
+ [ "$console" = "on" ] && PLUGINS="$PLUGINS -P'console $console_args'"
+ [ "$control" = "on" ] && PLUGINS="$PLUGINS -P'control $control_args'"
+ [ "$csf" = "on" ] && PLUGINS="$PLUGINS -P'csf'"
+ [ "$cutalot" = "on" ] && PLUGINS="$PLUGINS -P'cutalot'"
+ [ "$decruft" = "on" ] && PLUGINS="$PLUGINS -P'decruft'"
+ [ "$digicam" = "on" ] && PLUGINS="$PLUGINS -P'digicam'"
+ [ "$director" = "on" ] && PLUGINS="$PLUGINS -P'director'"
+ [ "$dummydevice" = "on" ] && PLUGINS="$PLUGINS -P'dummydevice'"
+ [ "$dvd" = "on" ] && PLUGINS="$PLUGINS -P'dvd $dvd_args'"
+ [ "$dvdconvert" = "on" ] && PLUGINS="$PLUGINS -P'dvdconvert $dvdconvert_args'"
+ [ "$dvdselect" = "on" ] && PLUGINS="$PLUGINS -P'dvdselect'"
+ [ "$dvdswitch" = "on" ] && PLUGINS="$PLUGINS -P'dvdswitch $dvdswitch_args'"
+ if [ "$epgsearch" = "on" ]; then
+ PLUGINS="$PLUGINS -P'epgsearch $epgsearch_args'"
+ [ "$epgsearchonly" = "on" ] && PLUGINS="$PLUGINS -P'epgsearchonly'"
+ [ "$conflictcheckonly" = "on" ] && PLUGINS="$PLUGINS -P'conflictcheckonly'"
+ [ "$quickepgsearch" = "on" ] && PLUGINS="$PLUGINS -P'quickepgsearch'"
+ fi
+ [ "$epgsync" = "on" ] && PLUGINS="$PLUGINS -P'epgsync'"
+ [ "$extb" = "on" ] && PLUGINS="$PLUGINS -P'extb $extb_args'"
+ [ "$extrecmenu" = "on" ] && PLUGINS="$PLUGINS -P'extrecmenu'"
+ [ "$femon" = "on" ] && PLUGINS="$PLUGINS -P'femon'"
+ [ "$ffnetdev" = "on" ] && PLUGINS="$PLUGINS -P'ffnetdev $ffnetdev_args'"
+ [ "$filebrowser" = "on" ] && PLUGINS="$PLUGINS -P'filebrowser'"
+ [ "$freecell" = "on" ] && PLUGINS="$PLUGINS -P'freecell'"
+ [ "$fritzbox" = "on" ] && PLUGINS="$PLUGINS -P'fritzbox'"
+ [ "$games" = "on" ] && PLUGINS="$PLUGINS -P'games'"
+ [ "$graphlcd" = "on" ] && PLUGINS="$PLUGINS -P'graphlcd $graphlcd_args'"
+ [ "$graphtft" = "on" ] && PLUGINS="$PLUGINS -P'graphtft $graphtft_args'"
+ [ "$image" = "on" ] && PLUGINS="$PLUGINS -P'image $image_args'"
+ [ "$ipod" = "on" ] && PLUGINS="$PLUGINS -P'ipod'"
+ [ "$iptv" = "on" ] && PLUGINS="$PLUGINS -P'iptv $iptv_args'"
+ [ "$launcher" = "on" ] && PLUGINS="$PLUGINS -P'launcher'"
+ [ "$lcdproc" = "on" ] && PLUGINS="$PLUGINS -P'lcdproc $lcdproc_args'"
+ [ "$lcr" = "on" ] && PLUGINS="$PLUGINS -P'lcr $lcr_args'"
+ [ "$live" = "on" ] && PLUGINS="$PLUGINS -P'live $live_args'"
+ [ "$loadepg" = "on" ] && PLUGINS="$PLUGINS -P'loadepg $loadepg_args'"
+ [ "$mailbox" = "on" ] && PLUGINS="$PLUGINS -P'mailbox $mailbox_args'"
+ [ "$manual" = "on" ] && PLUGINS="$PLUGINS -P'manual'"
+ [ "$mediamvp" = "on" ] && PLUGINS="$PLUGINS -P'mediamvp $mediamvp_args'"
+ [ "$menuorg" = "on" ] && PLUGINS="$PLUGINS -P'menuorg $menuorg_args'"
+ [ "$mlcd" = "on" ] && PLUGINS="$PLUGINS -P'mlcd'"
+ [ "$mlist" = "on" ] && PLUGINS="$PLUGINS -P'mlist'"
+ [ "$mousemate" = "on" ] && PLUGINS="$PLUGINS -P'mousemate $mousemate_args'"
+ [ "$mp3" = "on" ] && PLUGINS="$PLUGINS -P'mp3 $mp3_args'"
+ [ "$mplayer" = "on" ] && PLUGINS="$PLUGINS -P'mplayer $mplayer_args'"
+ [ "$muggle" = "on" ] && PLUGINS="$PLUGINS -P'muggle $muggle_args'"
+ [ "$music" = "on" ] && PLUGINS="$PLUGINS -P'music $music_args'"
+ [ "$coverviewer" = "on" ] && PLUGINS="$PLUGINS -P'coverviewer $coverviewer_args'"
+ [ "$netconfig" = "on" ] && PLUGINS="$PLUGINS -P'netconfig $netconfig_args'"
+ [ "$newsticker" = "on" ] && PLUGINS="$PLUGINS -P'newsticker'"
+ [ "$noepgmenu" = "on" ] && PLUGINS="$PLUGINS -P'noepgmenu'"
+ [ "$nordlichtsepg" = "on" ] && PLUGINS="$PLUGINS -P'nordlichtsepg'"
+ [ "$osdout" = "on" ] && PLUGINS="$PLUGINS -P'osdout'"
+ [ "$osdpip" = "on" ] && PLUGINS="$PLUGINS -P'osdpip'"
+ [ "$osdserver" = "on" ] && PLUGINS="$PLUGINS -P'osdserver'"
+ [ "$osdteletext" = "on" ] && PLUGINS="$PLUGINS -P'osdteletext $osdteletext_args'"
+ [ "$picselshow" = "on" ] && PLUGINS="$PLUGINS -P'picselshow $picselshow_args'"
+ [ "$pilot" = "on" ] && PLUGINS="$PLUGINS -P'pilot'"
+ [ "$pilotskin" = "on" ] && PLUGINS="$PLUGINS -P'pilotskin'"
+ [ "$pim" = "on" ] && PLUGINS="$PLUGINS -P'pim'"
+ [ "$pin" = "on" ] && PLUGINS="$PLUGINS -P'pin'"
+ [ "$playlist" = "on" ] && PLUGINS="$PLUGINS -P'playlist $playlist_args'"
+ [ "$podcatcher" = "on" ] && PLUGINS="$PLUGINS -P'podcatcher $podcatcher_args'"
+ [ "$powermate" = "on" ] && PLUGINS="$PLUGINS -P'powermate $powermate_args'"
+ [ "$prefermenu" = "on" ] && PLUGINS="$PLUGINS -P'prefermenu'"
+ [ "$premiereepg" = "on" ] && PLUGINS="$PLUGINS -P'premiereepg'"
+ [ "$pvrinput" = "on" ] && PLUGINS="$PLUGINS -P'pvrinput'"
+ [ "$radio" = "on" ] && PLUGINS="$PLUGINS -P'radio $radio_args'"
+ [ "$recstatus" = "on" ] && PLUGINS="$PLUGINS -P'recstatus'"
+ [ "$reelchannelscan" = "on" ] && PLUGINS="$PLUGINS -P'reelchannelscan'"
+ [ "$remote" = "on" ] && PLUGINS="$PLUGINS -P'remote $remote_args'" && chmod 0666 $remote_event
+ [ "$remoteosd" = "on" ] && PLUGINS="$PLUGINS -P'remoteosd'"
+ [ "$remotetimers" = "on" ] && PLUGINS="$PLUGINS -P'remotetimers'"
+ [ "$ripit" = "on" ] && PLUGINS="$PLUGINS -P'ripit'"
+ [ "$rotor" = "on" ] && PLUGINS="$PLUGINS -P'rotor $rotor_args'"
+ [ "$rssreader" = "on" ] && PLUGINS="$PLUGINS -P'rssreader'"
+ [ "$screenshot" = "on" ] && PLUGINS="$PLUGINS -P'screenshot'"
+ [ "$setup" = "on" ] && PLUGINS="$PLUGINS -P'setup'"
+ [ "$skinelchi" = "on" ] && PLUGINS="$PLUGINS -P'skinelchi $skinelchi_args'"
+ [ "$skinenigmang" = "on" ] && PLUGINS="$PLUGINS -P'skinenigmang $skinenigmang_args'"
+ [ "$skinreel" = "on" ] && PLUGINS="$PLUGINS -P'skinreel'"
+ [ "$skinsoppalusikka" = "on" ] && PLUGINS="$PLUGINS -P'skinsoppalusikka $skinsoppalusikka_args'"
+ [ "$sleeptimer" = "on" ] && PLUGINS="$PLUGINS -P'sleeptimer $sleeptimer_args'"
+ [ "$solitaire" = "on" ] && PLUGINS="$PLUGINS -P'solitaire'"
+ [ "$span" = "on" ] && PLUGINS="$PLUGINS -P'span'"
+ [ "$spider" = "on" ] && PLUGINS="$PLUGINS -P'spider'"
+ [ "$sport" = "on" ] && PLUGINS="$PLUGINS -P'sport $sport_args'"
+ [ "$streamdev_client" = "on" ] && PLUGINS="$PLUGINS -P'streamdev-client'"
+ [ "$streamdev_server" = "on" ] && PLUGINS="$PLUGINS -P'streamdev-server $streamdev_server_args'"
+ [ "$streamplayer" = "on" ] && PLUGINS="$PLUGINS -P'streamplayer'"
+ [ "$submenu" = "on" ] && PLUGINS="$PLUGINS -P'submenu'"
+ [ "$subtitles" = "on" ] && PLUGINS="$PLUGINS -P'subtitles'"
+ [ "$sudoku" = "on" ] && PLUGINS="$PLUGINS -P'sudoku'"
+ [ "$svdrpext" = "on" ] && PLUGINS="$PLUGINS -P'svdrpext'"
+ [ "$svdrpservice" = "on" ] && PLUGINS="$PLUGINS -P'svdrpservice'"
+ [ "$sysinfo" = "on" ] && PLUGINS="$PLUGINS -P'sysinfo'"
+ [ "$taste" = "on" ] && PLUGINS="$PLUGINS -P'taste'"
+ [ "$text2skin" = "on" ] && PLUGINS="$PLUGINS -P'text2skin'"
+ [ "$ttxtsubs" = "on" ] && PLUGINS="$PLUGINS -P'ttxtsubs'"
+ [ "$timeline" = "on" ] && PLUGINS="$PLUGINS -P'timeline'"
+ [ "$touchtft" = "on" ] && PLUGINS="$PLUGINS -P'touchtft $touchtft_args'"
+ [ "$tvonscreen" = "on" ] && PLUGINS="$PLUGINS -P'tvonscreen $tvonscreen_args'"
+ [ "$tvtv" = "on" ] && PLUGINS="$PLUGINS -P'tvtv'"
+ [ "$undelete" = "on" ] && PLUGINS="$PLUGINS -P'undelete $undelete_args'"
+ [ "$vdrcd" = "on" ] && PLUGINS="$PLUGINS -P'vdrcd $vdrcd_args'"
+ [ "$vdrrip" = "on" ] && PLUGINS="$PLUGINS -P'vdrrip $vdrrip_args'"
+ [ "$vompserver" = "on" ] && PLUGINS="$PLUGINS -P'vompserver $vompserver_args'"
+ [ "$wapd" = "on" ] && PLUGINS="$PLUGINS -P'wapd $wapd_args'"
+ [ "$weatherng" = "on" ] && PLUGINS="$PLUGINS -P'weatherng $weatherng_args'"
+ [ "$wirbelscan" = "on" ] && PLUGINS="$PLUGINS -P'wirbelscan'"
+ [ "$xxvautotimer" = "on" ] && PLUGINS="$PLUGINS -P'xxvautotimer $xxvautotimer_args'"
+ [ "$yaepg" = "on" ] && PLUGINS="$PLUGINS -P'yaepg'"
+ [ "$zaphistory" = "on" ] && PLUGINS="$PLUGINS -P'zaphistory'"
+ [ "$default_plugins" = "on" ] && PLUGINS="$PLUGINS -P'hello' -P'osddemo' -P'svccli' -P'svcsvr' -P'sky' -P'status' -P'svdrpdemo'"
+
+ # scripts
+ VDRRECCMDS="$VDRSCRIPTDIR/vdrreccmds"
+ VDRSETTIME="$VDRSCRIPTDIR/vdrsettime"
+ VDRSHUTDOWN="$VDRSCRIPTDIR/vdrshutdown"
+
+ # keyboard tty (terminal for vdr)
+ VDRKEYBTTY=""
+ if [ "$KEYB_TTY" != "" ]; then
+ VDRKEYBTTY="-t $KEYB_TTY"
+ [ "$VDRUSER" != "root" ] && chmod 0666 $KEYB_TTY
+ fi
+
+ # cmd
+ VDRCMD="$VDRPRG -w $WATCHDOG -v $VIDEODIR -c $VDRCONFDIR -E $VDRVARDIR/epg.data -L $VDRLIBDIR \
+ -s $VDRSHUTDOWN -T $VDRSETTIME -r $VDRRECCMDS $VDRKEYBTTY -u $VDRUSER $PLUGINS $OPTIONS "
+}
+
+# modules
+function get_modulenames() {
+ KVERS=`uname -r | grep -e '2.6'`
+ if [ -z "$KVERS" ]; then
+ MODULES=`lsmod | grep 'dvb-core' | cut -d'[' -f2 | cut -d']' -f1`
+ else
+ MODULES=`lsmod | grep '^dvb_core' | awk '{print $4;}' | awk '{ gsub(/,/," ", $1); print }'`
+ fi
+}
+
+get_modulenames
+
+if [ -z "$MODULES" ]; then # If no DVB-Modules were found, try to load
+ modprobe -q dvb # the module with the name dvb, this could
+ get_modulenames # be an alias for the dvb-ttpci-module or
+fi # another dvb-module
+
+MODULES="$MODULES dvb-core"
+
+if [ `echo $MODULES | grep -cw 'b2c2_flexcop'` -gt 0 ] && [ `echo $MODULES | grep -cw 'b2c2_flexcop_pci'` -eq 0 ]; then
+ MODULES="b2c2_flexcop_pci $MODULES"
+fi
+
+# clear TTY
+if [ "$KEYB_TTY" != "" ]; then
+ clear > $KEYB_TTY
+fi
+
+# main script
+while (true) do
+ # load vdr settings
+ vdr_settings
+ # start vdr
+ logger "Starting Video Disk Recorder at `date`"
+ eval $VDRCMD >/dev/null 2>&1
+ if test $? -eq 0; then
+ logger "Video Disk Recorder stopped at `date`"
+ exit 0
+ fi
+ # stop vdr
+ logger "Stopping Video Disk Recorder at `date`"
+ $KILL $VDRPRG
+ n=10
+ echo -n "Waiting $n seconds ..."
+ for i in `seq 1 $n`; do
+ sleep 1
+ (( n=$n-1 ))
+ echo -n " $n"
+ done
+ echo ""
+ # unload modules
+ for MODULE in $MODULES; do
+ rmmod $MODULE >/dev/null 2>&1
+ wait `pidof rmmod`
+ done
+ # load modules
+ for MODULE in $MODULES; do
+ modprobe $MODULE >/dev/null 2>&1
+ done
+done
+
+# clear tty
+if [ "$KEYB_TTY" != "" ]; then
+ clear > $KEYB_TTY
+fi
diff --git a/vdr/scripts/timerrep.sh b/vdr/scripts/timerrep.sh
new file mode 100644
index 0000000..c3d48bb
--- /dev/null
+++ b/vdr/scripts/timerrep.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+#------------------------------------------------------------------------------
+# this script allows searching for a repeat of timer using epgsearch (>=0.9.3).
+# it requires the timercmd patch from Gerhard Steiner, that extends the timers
+# menu of VDR with commands like in recordings menu
+#
+# add the following lines to your timercmds.conf
+#
+# Search for repeat : /path_of_this_script/timerrep.sh 0
+# Search for repeat (with subtitle): /path_of_this_script/timerrep.sh 1
+#
+# Author: Christian Wieninger (cwieninger@gmx.de)
+#------------------------------------------------------------------------------
+
+source /etc/default/vdr # x-vdr
+
+# adjust the following lines to your config
+# your plugins config dir
+PLUGINCONFDIR=${VDRCONFDIR}/plugins/epgsearch
+# path to svdrpsend.pl
+SVDRPSEND=${VDRBINDIR}/svdrpsend.pl
+# if you are using special subfolders for some recordings, please add them here
+FOLDERS=""
+# the key used to call epgsearch
+EPGSEARCHKEY=user2
+
+# do not edit below this line
+#------------------------------------------------------------------------------
+
+cat << EOM >/tmp/cmd.sh
+
+SEARCHTERM="$6"~"$7";
+#event info not yet present? then extract it from the file name
+if test "\$SEARCHTERM" == "~"; then
+SEARCHTERM='$8'
+#cut leading special folders
+i=0;
+FOLDERS=$FOLDERS;
+while [ "\$LASTWORD" != "\$FOLDERS" ];
+do
+ LASTWORD=\${FOLDERS%%,*};
+ SEARCHTERM=\${SEARCHTERM#*\$LASTWORD~};
+ i=\$i+1;
+ FOLDERS=\${FOLDERS#*,};
+done
+#cut trailing dummy subtitle created by epgsearch
+SEARCHTERM=\${SEARCHTERM%~???_??.??.????-??:??}
+if [ "$1" -eq "0" ]; then
+ SEARCHTERM=\${SEARCHTERM%~*};
+fi
+fi
+
+RCFILE=$PLUGINCONFDIR/.epgsearchrc;
+echo Search=\$SEARCHTERM > \$RCFILE;
+echo SearchMode=0 >> \$RCFILE;
+echo UseDescr=0 >> \$RCFILE;
+$SVDRPSEND HITK $EPGSEARCHKEY;
+EOM
+
+echo ". /tmp/cmd.sh; rm /tmp/cmd.sh" | at now
+
+
+
diff --git a/vdr/scripts/vdr b/vdr/scripts/vdr
new file mode 100644
index 0000000..c11d0a5
--- /dev/null
+++ b/vdr/scripts/vdr
@@ -0,0 +1,130 @@
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides: vdr
+# Required-Start: $remote_fs $syslog
+# Required-Stop: $remote_fs $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Linux Video Disk Recorder
+### END INIT INFO
+# x-vdr (Installations-Skript fuer einen VDR mit Debian als Basis)
+# von Marc Wernecke - www.zulu-entertainment.de
+# 12.02.2009
+# /etc/init.d/vdr
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DESC="Linux Video Disk Recorder"
+NAME="vdr"
+LOGGER="logger -s ${0##*/} "
+
+if [ -f /etc/default/vdr ]; then
+ . /etc/default/vdr
+else
+ $LOGGER "exit, because /etc/default/vdr does not exists"
+ exit 1
+fi
+
+DAEMON=$VDRBINDIR/runvdr
+PIDFILE=/var/run/runvdr.pid
+
+test -x "$DAEMON" || exit 0
+test -x "$VDRPRG" || exit 0
+
+function clean_console() {
+ if [ "$KEYB_TTY" != "" ]; then
+ tput -Tlinux clear > $KEYB_TTY
+ fi
+}
+
+function switch_console() {
+ if [ "$KEYB_TTY" != "" ]; then
+ clean_console
+ echo -e "This tty is currently mapped to VDR, keypresses aren't echoed.\r" > $KEYB_TTY
+ echo -n "For a login prompt switch to another console." > $KEYB_TTY
+ chvt `echo "$KEYB_TTY" | sed "s/\/dev\/tty//"`
+ REDIRECT="< $KEYB_TTY"
+ fi
+}
+
+function get_status() {
+ if start-stop-daemon --start --startas $DAEMON --test \
+ --name $(basename $DAEMON) --pidfile $PIDFILE >/dev/null
+ then
+ echo " - is not running."
+ exit 3
+ else
+ echo " - is running."
+ exit 0
+ fi
+}
+
+function startvdr() {
+ if [ "$AUTOSTART" != "off" ]; then
+ # only start vdr if there is no other instance running
+ if start-stop-daemon --start --startas $DAEMON --test \
+ --name $(basename $DAEMON) --pidfile $PIDFILE >/dev/null
+ then
+ # stopping dvb-bootsplash (mplayer)
+ [ -x /etc/init.d/dvb-bootsplash ] && /etc/init.d/dvb-bootsplash stop
+ # starting LCDd
+ # uncomment this only if you DONT use the systems init rc
+# [ "$LCDproc" = "on" ] && [ -f /etc/init.d/lcdd ] && /etc/init.d/lcdd start &
+ # starting Video Disk Recorder
+# switch_console
+ start-stop-daemon --start --quiet --startas $DAEMON --background \
+ --name $(basename $DAEMON) --pidfile $PIDFILE --make-pidfile -- $REDIRECT
+ else
+ echo -n " - seems to be running already"
+ fi
+ else
+ echo -n " - aborted (to enable the daemon, set AUTOSTART=\"on\" in /etc/default/vdr)"
+ fi
+}
+
+function stopvdr() {
+ if start-stop-daemon --stop --retry 30 \
+ --name $(basename $DAEMON) --pidfile $PIDFILE >/dev/null
+ then
+ start-stop-daemon --stop --retry 30 --oknodo --exec $VDRPRG >/dev/null
+ rm -f $PIDFILE
+ clean_console
+ # graphlcd shutdown sequence
+ [ "$graphlcd_base" = "on" ] && [ -f /etc/init.d/x-lcd ] && /etc/init.d/x-lcd stop
+ # stopping LCDd
+ # uncomment this only if you DONT use the systems init rc
+# [ "$LCDproc" = "on" ] && [ -f /etc/init.d/lcdd ] && /etc/init.d/lcdd stop
+ else
+ echo -n " - seems not to be running"
+ fi
+}
+
+case "$1" in
+ start)
+ echo -n "Starting $DESC: $NAME"
+ startvdr
+ echo "."
+ ;;
+ stop)
+ echo -n "Stopping $DESC: $NAME"
+ stopvdr
+ echo "."
+ ;;
+ restart|force-reload)
+ echo -n "Restarting $DESC: $NAME"
+ stopvdr
+ sleep 4
+ startvdr
+ echo "."
+ ;;
+ status)
+ echo -n "Getting status of $DESC: $NAME"
+ get_status
+ ;;
+ *)
+ N=/etc/init.d/$NAME
+ echo "Usage: $N {start|stop|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/vdr/scripts/vdr-no-shutdown b/vdr/scripts/vdr-no-shutdown
new file mode 100644
index 0000000..b880f93
--- /dev/null
+++ b/vdr/scripts/vdr-no-shutdown
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+while (true) do
+ date
+ echo "vdr-no-shutdown"
+ echo "if this script is running"
+ echo "vdr can not shutdown the system"
+ sleep 60
+ done
+
+
+
diff --git a/vdr/scripts/vdr2root b/vdr/scripts/vdr2root
new file mode 100644
index 0000000..e6cea06
--- /dev/null
+++ b/vdr/scripts/vdr2root
@@ -0,0 +1,244 @@
+#!/bin/sh
+
+# x-vdr (Installations-Skript fuer einen VDR mit Debian als Basis)
+# von Marc Wernecke - www.zulu-entertainment.de
+# 24.03.2008
+
+# vdr2root
+
+# Load VDR configuration
+GRUB_POWEROFF=
+. /etc/default/vdr
+LOGGER="logger -s ${0##*/} "
+
+# Load vdrshutdown configuration
+if [ -f "$VDRCONFDIR/shutdown.conf" ]; then
+ . "$VDRCONFDIR/shutdown.conf"
+else
+ $LOGGER "$VDRCONFDIR/shutdown.conf nonreadable"
+ $LOGGER "using defaults"
+fi
+
+KILL="/usr/bin/killall -q -TERM"
+
+if [ $# -eq 0 ]; then
+ echo "vdr2root -> this script needs a command..."
+ $0 help
+ exit 1
+fi
+
+case $1 in
+ admin)
+ $VDRCONFDIR/plugins/admin/setvdr.sh
+ ;;
+ clean-log)
+ cd /var/log ; cat /dev/null > messages ; cat /dev/null > wtmp ; echo "Säubern der Logfiles..."
+ ;;
+ show-log)
+ if test -s /var/log/messages ; then tail -n 500 /var/log/messages|tac ; else echo "(/var/log/messages) nicht gefunden oder leer..." ; fi
+ ;;
+ video-update)
+ touch $VIDEODIR/.update
+ ;;
+ ssh-start)
+ /etc/init.d/ssh start
+ ;;
+ ssh-stop)
+ /etc/init.d/ssh stop
+ ;;
+ ssh-restart)
+ /etc/init.d/ssh restart
+ ;;
+ vdr-kill)
+ killall -9 runvdr
+ killall -9 vdr
+ ;;
+ vdr-start)
+ /etc/init.d/vdr start
+ ;;
+ vdr-stop)
+ if [ "$AUTOSTART" != "off" ]; then
+ /etc/init.d/vdr stop
+ else
+ killall -9 runvdr
+ killall -9 vdr
+ fi
+ ;;
+ vdr-restart)
+ if [ "$AUTOSTART" != "off" ]; then
+ /etc/init.d/vdr restart
+ else
+ killall -9 vdr
+ fi
+ ;;
+ acpi-wakeup)
+ # $2 = wakeuptime
+ [ $# -eq 3 ] && echo $2 > $3
+ ;;
+ nvram-wakeup)
+ # $2 = wakeuptime
+ [ $# -eq 2 ] && nvram-wakeup $2
+ ;;
+ extb-poweroff)
+ $VDRSCRIPTDIR/extb-poweroff.pl
+ ;;
+ lilo)
+ lilo -R PowerOff
+ ;;
+ grub)
+ if [ $# -eq 2 ] && [ -n "$GRUB_POWEROFF" ]; then
+ grub-set-default $GRUB_POWEROFF
+ else
+ $LOGGER "stop shutdown, because missing configuration"
+ fi
+ ;;
+ reboot)
+ shutdown -r now
+ ;;
+ shutdown)
+ shutdown -h now
+ ;;
+ poweroff)
+ poweroff
+ ;;
+ settime)
+ [ $# -eq 2 ] && /bin/date $2
+ ;;
+ readdvd)
+ # dvdselect and dvdswitch
+ # $1 = "readdvd"
+ # $2 = directory for dvd's (see plugin configuration menu)
+ # $3 = name of dvd
+ # $4 = original dvd-device
+ # dvdswitch only
+ # $5 = mountpoint - default is "(null)"
+ # $6 = "IMAGE" or "DIR"
+ LOGFILE="/var/log/vdrreaddvd.log"
+ echo "vdr2root # `date`" > "$LOGFILE" 2>&1
+ echo "vdr2root # $0 $*" >> "$LOGFILE" 2>&1
+ if [ ! -e "$DVDBURNER" ]; then
+ echo "vdr2root # DVDBURNER=\"$DVDBURNER\" does not exists" >> "$LOGFILE" 2>&1
+ exit 1
+ fi
+ eject -t $DVDBURNER
+ [ ! -d "$2" ] && mkdir -p "$2" && echo "vdr2root # Erstelle \"$2\"" >> "$LOGFILE" 2>&1
+ case $6 in
+ IMAGE)
+ [ -f "$2/$3" ] && rm -f "$2/$3" && echo "vdr2root # Lösche \"$2/$3\"" >> "$LOGFILE" 2>&1
+ echo "vdr2root # DVD einlesen gestartet" >> "$LOGFILE" 2>&1
+ $VDRBINDIR/svdrpsend.pl -d localhost "MESG DVD einlesen gestartet"
+ dd if="$4" of="$2/$3" >> "$LOGFILE" 2>&1
+ ;;
+ DIR)
+ cd "$2"
+ [ -d "$3" ] && rm -rf "$3" && echo "vdr2root # Lösche \"$2/$3\"" >> "$LOGFILE" 2>&1
+ echo "vdr2root # DVD einlesen gestartet" >> "$LOGFILE" 2>&1
+ $VDRBINDIR/svdrpsend.pl -d localhost "MESG DVD einlesen gestartet"
+ vobcopy -i $DVDBURNER -o "$2" -t "$3" -m >> "$LOGFILE" 2>&1
+ ;;
+ esac
+ eject $DVDBURNER
+ echo "vdr2root # DVD einlesen fertig" >> "$LOGFILE" 2>&1
+ chown $VDRUSER.$VDRGROUP "$2/$3" && echo "vdr2root # chown $VDRUSER.$VDRGROUP \"$2/$3\"" >> "$LOGFILE" 2>&1
+ $VDRBINDIR/svdrpsend.pl -d localhost "MESG DVD einlesen fertig"
+ ;;
+ writedvd)
+ # dvdselect and dvdswitch
+ # $1 = writedvd
+ # $2 = filename
+ LOGFILE="/var/log/vdrwritedvd.log"
+ echo "vdr2root # `date`" > "$LOGFILE" 2>&1
+ echo "vdr2root # $0 $1 \"$2\"" >> "$LOGFILE" 2>&1
+ if [ ! -e "$DVDBURNER" ]; then
+ echo "vdr2root # DVDBURNER=\"$DVDBURNER\" does not exists" >> "$LOGFILE" 2>&1
+ exit 1
+ fi
+ BURN_SPEED=0
+ SPEED=""
+ [ $BURN_SPEED -gt 0 ] && SPEED="-speed=$BURN_SPEED"
+ unset SUDO_COMMAND
+ eject -t $DVDBURNER
+ echo "vdr2root # DVD brennen gestartet" >> "$LOGFILE" 2>&1
+ $VDRBINDIR/svdrpsend.pl -d localhost "MESG DVD brennen gestartet"
+ if [ -f "$2" ]; then
+ growisofs -use-the-force-luke=tty -dvd-compat $SPEED -Z $DVDBURNER="$2" >> "$LOGFILE" 2>&1
+ elif [ -d "$2" ]; then
+ DVDNAME=`basename "$2"`
+ growisofs -use-the-force-luke=tty -dvd-compat $SPEED -Z $DVDBURNER -dvd-video -V "$DVDNAME" "$2" >> "$LOGFILE" 2>&1
+ fi
+ eject $DVDBURNER
+ echo "vdr2root # DVD brennen fertig" >> "$LOGFILE" 2>&1
+ $VDRBINDIR/svdrpsend.pl -d localhost "MESG DVD brennen fertig"
+ ;;
+ automount)
+ wakeonlan 00:00:00:00:00:00
+ echo "wake on..."
+ sleep 10
+ [ ! -d /mnt/vdrserver ] && mkdir -p /mnt/vdrserver
+ if [ ! -d $VIDEODIR/vdrserver ]; then
+ cd $VIDEODIR
+ ln -vnfs /mnt/vdrserver vdrserver
+ fi
+ echo "...waiting"
+ sleep 10
+ echo "waiting..."
+ sleep 10
+ echo "...waiting"
+ sleep 10
+ echo "waiting..."
+ sleep 10
+ echo "...mounting"
+ mount -t smbfs -o password= //vdrserver/video /mnt/vdrserver
+ echo "erneues einlesen der Aufnahmen..."
+ touch $VIDEODIR/.update
+ ;;
+ wakeonlan)
+ wakeonlan 00:00:00:00:00:00
+ ;;
+ mount)
+ if [ ! -d /mnt/vdrserver ] ; then mkdir -p /mnt/vdrserver ; fi
+ if [ ! -d $VIDEODIR/vdrserver ] ; then
+ cd $VIDEODIR
+ ln -vnfs /mnt/vdrserver vdrserver
+ fi
+ mount -t smbfs -o password= //vdrserver/video /mnt/vdrserver
+ touch $VIDEODIR/.update
+ ;;
+ umount)
+ umount -t smbfs /mnt/vdrserver
+# exec umount -t smbfs /mnt/vdrserver
+ touch $VIDEODIR/.update
+ ;;
+ * | help)
+ [ "$1" != "help" ] && echo "vdr2root -> # $0 $* <- command not found"
+ echo "vdr2root -> possible commands are..."
+ echo "$0 acpi-wakeup \$2"
+ echo "$0 admin"
+ echo "$0 automount"
+ echo "$0 clean-log"
+ echo "$0 help"
+ echo "$0 lilo"
+ echo "$0 mount"
+ echo "$0 nvram-wakeup \$2"
+ echo "$0 poweroff"
+ echo "$0 readdvd \$2 \$3 \$4"
+ echo "$0 reboot"
+ echo "$0 settime \$2"
+ echo "$0 show-log"
+ echo "$0 shutdown"
+ echo "$0 ssh-start"
+ echo "$0 ssh-stop"
+ echo "$0 umount"
+ echo "$0 vdr-start"
+ echo "$0 vdr-stop"
+ echo "$0 vdr-restart"
+ echo "$0 video-update"
+ echo "$0 wakeonlan"
+ echo "$0 writedvd \$2"
+ ;;
+esac
+
+exit 0
+
+
+
diff --git a/vdr/scripts/vdrmount b/vdr/scripts/vdrmount
new file mode 100644
index 0000000..0be7b02
--- /dev/null
+++ b/vdr/scripts/vdrmount
@@ -0,0 +1,35 @@
+#!/bin/bash
+#
+# This script is called from VDR to mount/unmount/eject
+# the sources for MP3 play.
+#
+# argument 1: wanted action, one of mount,unmount,eject,status
+# argument 2: mountpoint to act on
+#
+# mount,unmount,eject must return 0 if succeeded, 1 if failed
+# status must return 0 if device is mounted, 1 if not
+#
+
+action="$1"
+path="$2"
+
+case "$action" in
+ mount)
+ if [ -z "$(echo $path | grep "usb")" ]; then
+ eject -t "$path" || exit 1 # close the tray
+ fi
+ mount "$path" || exit 1 # mount it
+ ;;
+ unmount)
+ umount "$path" || exit 1 # unmount it
+ ;;
+ eject)
+ eject "$path" || exit 1 # eject disk
+ ;;
+ status)
+ cat /proc/mounts | grep -q "$path" # check if mounted
+ [ $? -ne 0 ] && exit 1 # not mounted ...
+ ;;
+esac
+
+exit 0
diff --git a/vdr/scripts/vdrnoad b/vdr/scripts/vdrnoad
new file mode 100644
index 0000000..39ba8af
--- /dev/null
+++ b/vdr/scripts/vdrnoad
@@ -0,0 +1,99 @@
+#!/bin/bash
+#
+# vdrnoad
+#
+# Required: noad, at
+#
+# add this lines to your reccmds.conf:
+# folgende zeilen in die reccmds.conf eintragen:
+#
+# Run noad : /usr/lib/vdr/scripts/vdrnoad -start
+# Run noad, all recordings (batch) : /usr/lib/vdr/scripts/vdrnoad -batch
+# View Cut list : /usr/lib/vdr/scripts/vdrnoad -view
+# In prozess : /usr/lib/vdr/scripts/vdrnoad -count
+# Stop noad (killpid) : /usr/lib/vdr/scripts/vdrnoad -killpid
+# Stop noad (killall)? : /usr/lib/vdr/scripts/vdrnoad -killall
+# Remove marks : /usr/lib/vdr/scripts/vdrnoad -marks
+# Remove pid : /usr/lib/vdr/scripts/vdrnoad -pid
+# Remove all marks : /usr/lib/vdr/scripts/vdrnoad -marks.vdr
+# Remove all pids : /usr/lib/vdr/scripts/vdrnoad -noad.pid
+
+# CONFIG START
+ NOADOPTS="nice --background --ac3 --asd --overlap --jumplogo --comments --OSD --statisticfile=/var/log/noadstat"
+ #VIDEODIR="/video"
+ source /etc/default/vdr
+# CONFIG END
+
+#PATH=/usr/local/bin:$PATH
+
+case "$1" in
+ -start)
+ echo "recording: "
+ echo
+ echo "$2"
+ echo
+ if [ -e "$2/noad.pid" ] ; then
+ echo "noad is already active..."
+ else
+ echo "noad $NOADOPTS \"$2\" >/dev/null 2>&1" | at now
+ echo "noad startet..."
+ fi
+ ;;
+ -batch)
+ while read i ; do
+ if [ ! -e "$i/marks.vdr" -a ! -e "$i/noad.pid" ] ; then
+ echo "noad $NOADOPTS \"$i\" >/dev/null 2>&1" | batch
+ fi
+ done < <(find "$VIDEODIR" -follow -name "*.rec")
+ ;;
+ -view)
+ echo "recording: "
+ echo
+ echo "$2"
+ echo
+ if [ -e "$2/noad.pid" ] ; then
+ echo "noad is still active..."
+ echo
+ fi
+ if [ -e "$2/marks.vdr" ] ; then
+ cat "$2/marks.vdr"
+ else
+ echo "cut list not found..."
+ fi
+ ;;
+ -count)
+ echo "all noad processes:"
+ echo
+ pidof noad | wc -w | tr -d ' '
+ ;;
+ -killpid)
+ echo "recording: "
+ echo
+ echo "$2"
+ echo
+ if [ -e "$2/noad.pid" ] ; then
+ kill `cat "$2/noad.pid"`
+ echo "noad stopped..."
+ else
+ echo "noad is not active..."
+ fi
+ ;;
+ -killall)
+ PID=`pidof noad`
+ eval ${PID:+kill $PID}
+ ;;
+ -marks)
+ rm -f "$2/marks.vdr"
+ ;;
+ -pid)
+ rm -f "$2/noad.pid"
+ ;;
+ -marks.vdr|-noad.pid)
+ find "$VIDEODIR" -follow -type f -name "${1/-}" -exec rm -f '{}' ';' &
+ ;;
+ *)
+ echo "unrecognized option: $1"
+ exit 1
+ ;;
+esac
+
diff --git a/vdr/scripts/vdrplayer b/vdr/scripts/vdrplayer
new file mode 100644
index 0000000..ce6b8b5
--- /dev/null
+++ b/vdr/scripts/vdrplayer
@@ -0,0 +1,279 @@
+#!/bin/bash
+
+# vdrplayer 0.0.5 (start-script for shmclient, tvtime, vdrtvtime, vdr-sxfe, xine or xineliboutput)
+# by Marc Wernecke - www.zulu-entertainment.de
+# 10.02.2009
+
+### Config ####################################################################################
+NONROOT="true" # true or false
+STARTX="true" # allways, true or false
+VDRIP= # 127.0.0.1 for localhost or any other ip for a vdr-server in your local-network
+VDRPLAYER= # shmclient, tvtime, vdr-sxfe, xine, xineliboutput or xine-network
+
+[ "$HOME" = "/" ] && HOME=/etc
+[ -r $HOME/vdrplayer.conf ] && . $HOME/vdrplayer.conf
+
+### Arguments #################################################################################
+EXIT="false"
+KILLALL="false"
+VERSION="vdrplayer 0.0.4"
+
+for i in $* ; do
+ case $1 in
+ --exit|-e)
+ EXIT="true"
+ ;;
+ --help|-h)
+ echo "$VERSION"
+ echo "--exit|-e Kill all active player and exit"
+ echo "--help|-h Shows the help text and exit"
+ echo "--killall|-k Kill all active player, the X server and exit"
+ echo "--nonroot|-n Sets NONROOT flag"
+ echo "--reset|-r Remove the configuration file (\$HOME/vdrplayer.conf)"
+ echo "--startx|-s [allways|true|false]"
+ echo "--ip|-i [ip] 127.0.0.1 for localhost or any other ip for a vdr-server in your local-network"
+ echo "--player|-p [shmclient|tvtime|vdr-sxfe|xine|xineliboutput|xine-network]"
+ echo "--version|-v Shows the script version and exit"
+ exit 0
+ ;;
+ --killall|-k)
+ KILLALL="true"
+ ;;
+ --nonroot|-n)
+ NONROOT="true"
+ ;;
+ --reset|-r)
+ if [ -f $HOME/vdrplayer.conf ]; then
+ rm -f $HOME/vdrplayer.conf
+ VDRIP=
+ VDRPLAYER=
+ fi
+ ;;
+ --startx|-s)
+ shift 1
+ STARTX="$1"
+ ;;
+ --ip|-i)
+ shift 1
+ VDRIP="$1"
+ ;;
+ --player|-p)
+ shift 1
+ VDRPLAYER="$1"
+ ;;
+ --version|-v)
+ echo "$VERSION"
+ exit 0
+ ;;
+ esac
+ shift 1
+done
+
+if [ -n "`ps x --user root | grep "/usr/.*bin/X.*:0" | grep -v grep`" ]; then # X is running
+ DIALOG="Xdialog"
+else
+ DIALOG="dialog"
+fi
+
+### Functions #################################################################################
+function _exit() {
+ case $1 in
+ 0) exit 0
+ ;;
+ 1) exit 1
+ ;;
+ 2) exit 2
+ ;;
+ esac
+ exit 0
+}
+
+function _vdr() {
+ # Max waiting time in Seconds
+ n=10
+ # Check if VDR is running
+ if [ `ps -C vdr | grep -cw "vdr"` -eq 0 ]; then
+ echo "vdr is not running"
+ echo -n "Waiting $n seconds ..."
+ # test if vdr is running
+ while [ `ps -C vdr | grep -cw "vdr"` -eq 0 ] && [ $n -gt 0 ]; do
+ sleep 1
+ (( n=$n-1 ))
+ echo -n " $n"
+ if [ $n -eq 0 ]; then
+ echo " exit"
+ return 1
+ fi
+ done
+ echo ""
+ echo "vdr is online"
+ fi
+ return 0
+}
+
+function _vdrip() {
+ VDRIP=`$DIALOG --inputbox "VDR Server IP" 10 50 "127.0.0.1" 3>&1 1>&2 2>&3`
+ [ $? != 0 ] && return 1
+ [ "$VDRIP" = "" ] && return 1
+ echo "VDRIP=$VDRIP" >> $HOME/vdrplayer.conf
+ return 0
+}
+
+function _x() {
+ case $STARTX in
+ allways) STARTX="allways";;
+ true) STARTX="true";;
+ *) STARTX="false"; return 0 ;;
+ esac
+ VT=vt7
+
+ # if no X is running and user=root, start it
+ if [ -n "`ps x --user root | grep "/usr/.*bin/X.*:1" | grep -v "grep"`" ]; then
+ # X:1 is running
+ export DISPLAY=:1
+ return 0
+ elif [ -n "`ps x --user root | grep "/usr/.*bin/X" | grep -v "grep"`" ]; then
+ # X:0 is running
+ if [ "$STARTX" = "allways" ]; then
+ if [ "$NONROOT" = "false" ]; then
+ [ "`id -u`" = "0" ] || return 1
+ fi
+ export DISPLAY=:1
+ VT=vt9
+ else
+ export DISPLAY=:0
+ return 0
+ fi
+ else
+ if [ "$NONROOT" = "false" ]; then
+ [ "`id -u`" = "0" ] || return 1
+ fi
+ export DISPLAY=:0
+ fi
+
+ # start X
+ echo "Starting the X server..."
+ /usr/bin/X -ac -dpi 100 $DISPLAY $VT & sleep 4
+# xhost local:localhost
+ return 0
+}
+
+function _shmclient() {
+ _x || return 1
+ CMD="--fullscreen"
+ ShmClient $CMD &
+ return 0
+}
+
+function _tvtime() {
+# chmod 666 /dev/rtc
+# echo 1024 > /proc/sys/dev/rtc/max-user-freq
+ _x || return 1
+ CMD="--fullscreen --display=$DISPLAY"
+ tvtime $CMD &
+ sleep 2
+ tvtime-command MENU_EXIT
+ return 0
+}
+
+function _vdrtvtime() {
+# chmod 666 /dev/rtc
+# echo 1024 > /proc/sys/dev/rtc/max-user-freq
+ _x || return 1
+ svdrpsend.pl PLUG vdrtvtime start &
+ return 0
+}
+
+function _vdrsxfe() {
+ [ -n "$VDRIP" ] || _vdrip || return 1
+ _x || return 1
+ CMD="--video=xv --fullscreen --post tvtime:method=LinearBlend,cheap_mode=1,pulldown=0,use_progressive_frame_flag=1"
+ MRL="xvdr+tcp://$VDRIP"
+ vdr-sxfe $CMD $MRL &
+ return 0
+}
+
+function _xineliboutput() {
+ [ -n "$VDRIP" ] || _vdrip || return 1
+ _x || return 1
+ CMD="--fullscreen --hide-gui"
+ MRL="xvdr+tcp://$VDRIP#nocache;demux:mpeg_block"
+ xine $CMD "$MRL" &
+ return 0
+}
+
+function _xine() {
+ _x || return 1
+ CMD="--fullscreen --hide-gui --post vdr --post vdr_audio"
+ MRL="vdr:/tmp/vdr-xine/stream#demux:mpeg_pes"
+ xine $CMD "$MRL" &
+ return 0
+}
+
+function _xinenetwork() {
+ [ -n "$VDRIP" ] || _vdrip || return 1
+ _x || return 1
+ CMD="--fullscreen --hide-gui --post vdr --post vdr_audio"
+ MRL="netvdr://$VDRIP#demux:mpeg_pes"
+ xine $CMD "$MRL" &
+ return 0
+}
+
+### Main Script ###############################################################################
+
+_vdr || exit 1 # Check if VDR is running
+
+[ -n "`ps x | grep ShmClient`" ] && killall -q -9 ShmClient # If ShmClient is running, stop it
+[ -n "`ps x | grep tvtime`" ] && killall -q -9 tvtime # If tvtime is running, stop it
+[ -n "`ps x | grep vdr-sxfe`" ] && killall -q -9 vdr-sxfe # If vdr-sxfe is running, stop it
+[ -n "`ps cx | grep xine`" ] && killall -q -9 xine # If xine is running, stop it
+[ "$VDRPLAYER" = "vdrtvtime" ] && svdrpsend.pl PLUG vdrtvtime stop
+
+if [ "$EXIT" = "true" ]; then # Exit
+ exit 0
+fi
+
+if [ "$KILLALL" = "true" ]; then # Stop X and Exit
+ [ -n "`ps x --user root | grep "/usr/.*bin/X" | grep -v "grep"`" ] && killall -q -9 Xorg
+ exit 0
+fi
+
+# Player
+if [ -n "$VDRPLAYER" ]; then
+ case $VDRPLAYER in
+ shmclient) _shmclient ;;
+ tvtime) _tvtime ;;
+ vdrtvtime) _vdrtvtime ;;
+ vdr-sxfe) _vdrsxfe ;;
+ xineliboutput) _xineliboutput ;;
+ xine) _xine ;;
+ xine-network) _xinenetwork ;;
+ *) VDRPLAYER="" ;;
+ esac
+fi
+
+# Dialog
+if [ -z "$VDRPLAYER" ]; then
+ option=`$DIALOG --menu \
+ 'Aktiviere den Player, den du nutzen moechtest.' 0 0 8 \
+ '1' 'ShmClient (Softdevice)' \
+ '2' 'Tvtime (FF-Karte)' \
+ '3' 'vdrtvtime (tvtime als Plugin, FF-Karte)' \
+ '4' 'vdr-sxfe (Xineliboutput)' \
+ '5' 'Xine' \
+ '6' 'Xine (Xineliboutput)' \
+ '7' 'Xine-Network' 3>&1 1>&2 2>&3`
+ [ $? != 0 ] && exit 1
+ VDRIP=
+ case $option in
+ 1) echo "VDRPLAYER=\"shmclient\"" > $HOME/vdrplayer.conf && _shmclient ;;
+ 2) echo "VDRPLAYER=\"tvtime\"" > $HOME/vdrplayer.conf && _tvtime ;;
+ 3) echo "VDRPLAYER=\"vdrtvtime\"" > $HOME/vdrplayer.conf && _vdrtvtime ;;
+ 4) echo "VDRPLAYER=\"vdr-sxfe\"" > $HOME/vdrplayer.conf && _vdrsxfe ;;
+ 5) echo "VDRPLAYER=\"xine\"" > $HOME/vdrplayer.conf && _xine ;;
+ 6) echo "VDRPLAYER=\"xineliboutput\"" > $HOME/vdrplayer.conf && _xineliboutput ;;
+ 7) echo "VDRPLAYER=\"xine-network\"" > $HOME/vdrplayer.conf && _xinenetwork ;;
+ esac
+fi
+
+exit 0 \ No newline at end of file
diff --git a/vdr/scripts/vdrreaddvd b/vdr/scripts/vdrreaddvd
new file mode 100644
index 0000000..ff1a84d
--- /dev/null
+++ b/vdr/scripts/vdrreaddvd
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# vdrreaddvd - x-vdr version
+#
+# This script will be called by the vdr-plugins dvdselect and dvdswitch to copy a DVD to the local drive.
+#
+# It gets the following parameters:
+#
+# dvdselect and dvdswitch
+# $1 = directory for dvd's (see plugin configuration menu)
+# $2 = name of dvd
+# $3 = original dvd-device
+# dvdswitch only
+# $4 = mountpoint - default is "(none)"
+# $5 = "IMAGE" or "DIR"
+
+# Load VDR configuration
+. /etc/default/vdr
+
+# vdr2root
+if [ $# -eq 3 ]; then
+ # dvdselect
+ echo "sudo $VDRSCRIPTDIR/vdr2root readdvd \"$1\" \"$2\" \"$3.iso\" (none) IMAGE" | at now
+elif [ $# -eq 5 ]; then
+ # dvdswitch
+ echo "sudo $VDRSCRIPTDIR/vdr2root readdvd \"$1\" \"$2\" \"$3\" \"$4\" \"$5\"" | at now
+else
+ exit 1
+fi
+
+exit 0
diff --git a/vdr/scripts/vdrreccmds b/vdr/scripts/vdrreccmds
new file mode 100644
index 0000000..5259b43
--- /dev/null
+++ b/vdr/scripts/vdrreccmds
@@ -0,0 +1,32 @@
+#!/bin/sh
+# 24.06.2007
+
+# Load VDR configuration
+. /etc/default/vdr
+
+function NoadExclude() {
+ Channel="$*"
+ [ -r $VDRCONFDIR/noad-exclude-channels.conf ] || return 1
+ cat $VDRCONFDIR/noad-exclude-channels.conf | while read NewLine; do
+ [ -n "`echo "$Channel" | grep "D\ .*\ -"`" ] && Channel="`echo "$Channel" | cut -c1,2,3`"
+ [ "$NewLine" = "$Channel" ] && killall -9 $(basename $0)
+ done
+}
+
+case "$1" in
+ before)
+ echo "Start recording $2"
+ ;;
+ after)
+ echo "Finished recording $2"
+ [ "$noad" = "on" ] || exit
+ [ $# -ge 3 ] && NoadExclude "$3"
+ $VDRSCRIPTDIR/vdrnoad -start $2
+ ;;
+ edited)
+ echo "Edited recording $2"
+ ;;
+ *)
+ echo "ERROR: unknown state: $1"
+ ;;
+esac \ No newline at end of file
diff --git a/vdr/scripts/vdrsettime b/vdr/scripts/vdrsettime
new file mode 100644
index 0000000..6bd83bb
--- /dev/null
+++ b/vdr/scripts/vdrsettime
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# Load VDR configuration
+. /etc/default/vdr
+
+sudo $VDRSCRIPTDIR/vdr2root settime "$1"
+
+
diff --git a/vdr/scripts/vdrsetup b/vdr/scripts/vdrsetup
new file mode 100644
index 0000000..e45e39f
--- /dev/null
+++ b/vdr/scripts/vdrsetup
@@ -0,0 +1,1896 @@
+#!/bin/bash
+#
+# (x-)vdrsetup 0.0.8 - script for OSDServer on localhost
+# by Marc Wernecke - www.zulu-entertainment.de
+# 12.02.2009
+
+script="$0"
+
+# convenient return values
+true=0
+false=1
+
+
+# Some generic stuff for handling a connection
+
+function error() {
+ # Fatal error. Send quit command, close FIFO and terminate netcat
+ DoLog "fatal error, disconnecting..."
+ [ "${reply2xx[0]}" != 202 ] && SendCmd Quit
+
+ exec 3>&-
+ exec 4>&-
+
+ kill $pid
+
+ exit 1
+}
+
+function ConnectServer() {
+ # Connect to the OSDServer
+ DoLog "connecting to the OSDServer..."
+ # Set up temporary fifo and open as file descriptor 3 and 4
+ mkfifo --mode=700 /tmp/pipe-in$$ /tmp/pipe-out$$
+ exec 3<> /tmp/pipe-in$$
+ exec 4<> /tmp/pipe-out$$
+ rm /tmp/pipe-in$$ /tmp/pipe-out$$
+
+ # Connect to server using the fifo
+ {
+ netcat $1 $2
+
+ # In case of connection loss:
+ echo 499 disconnected
+ echo 202 Good Bye.
+ } <&3 >&4 &
+ pid=$!
+
+ # Sending to the server: use >&3
+ # Receive from the server: use <&4
+}
+
+
+function ReadReply() {
+ # Read a complete server reply until 2xx return code,
+ # and store replies in each category by number
+ reply2xx=()
+ reply3xx=()
+ reply4xx=()
+ reply5xx=()
+ reply6xx=()
+
+ while read -r code line <&4 ; do
+ echo "< $code $line"
+ # screen echo
+
+ # vdrsetup debug
+ if [ "$vdrsetup_use_debug" = "on" ]; then
+ [ "$vdrsetup_use_log" = "on" ] && echo "< $code $line" >> "$vdrsetup_log"
+ [ "$vdrsetup_use_syslog" = "on" ] && $LOGGER "< $code $line"
+ fi
+
+ case $code in
+ 2*) IFS=$' \t\n\r' reply2xx=($code "$line")
+ ;;
+ 3*) IFS=$' \t\n\r' reply3xx=($code $line)
+ ;;
+ 4*) IFS=$' \t\n\r' reply4xx=($code "$line")
+ ;;
+ 5*) IFS=$' \t\n\r' reply5xx=($code "$line")
+ ;;
+ 6*) IFS=$' \t\n\r' reply6xx=($code "$line")
+ ;;
+ esac
+ [ -n "${reply2xx[0]}" ] && break
+ done
+
+ [ -n "${reply4xx[0]}" ] && return $false
+ return $true
+}
+
+function SendCmd() {
+ # Send a command and read the reply
+
+ echo "> $*"
+ # screen echo
+
+ echo "$*" >&3
+ # network send
+
+ # vdrsetup debug
+ if [ "$vdrsetup_use_debug" = "on" ]; then
+ [ "$vdrsetup_use_log" = "on" ] && echo "> $*" >> "$vdrsetup_log"
+ [ "$vdrsetup_use_syslog" = "on" ] && $LOGGER "> $*"
+ fi
+
+ ReadReply
+}
+
+function IsEvent() {
+ # Helper to check reply for a certain event
+
+ [ "${reply3xx[0]}" != 300 ] && return $false
+ [ "${reply3xx[1]}" != "$1" ] && return $false
+ [ "${reply3xx[2]}" != "$2" ] && return $false
+
+ return $true
+}
+
+
+function QuoteString() {
+ # Quote arbitrary string for use in '' and ""
+ local str="${!1}"
+
+ str="${str//'\'/\\\\}"
+ str="${str//'\\'/\\\\}"
+ # work around bash bug: double quoted '\'
+
+ str="${str//\'/$'\\\''}"
+ # This is bogus, anyone knows something better to replace ' by \' ?
+
+ str="${str//\"/\\\"}"
+ str="${str//$'\r'/\\r}"
+ str="${str//$'\n'/\\n}"
+ str="${str//$'\t'/\\t}"
+
+ eval "$1=\$str"
+}
+
+function UnquoteString() {
+ # Unquote string
+ local str="${!1}"
+
+ str="${str//\\r/$'\r'}"
+ str="${str//\\n/$'\n'}"
+ str="${str//\\t/$'\t'}"
+ str="${str//\\\"/\"}"
+ str="${str//\\\'/\'}"
+ str="${str//\\\\/\\}"
+
+ eval "$1=\$str"
+}
+
+
+##############
+# vdrsetup #
+##############
+vdrsetup_ok_ends="on"
+vdrsetup_key_timeout=1000
+# logging
+LOGGER="logger -- ${0##*/} - "
+vdrsetup_use_screen="on" # echo setup changings to the screen
+vdrsetup_use_log="off" # echo setup changings to $vdrsetup_log
+vdrsetup_log="/tmp/vdrsetup.log" # log file
+vdrsetup_use_syslog="on" # echo setup changings (with LOGGER) to syslog
+vdrsetup_use_debug="off" # enable comunications log
+
+# vdr configuration
+config=/etc/default/vdr # path to vdr configuration
+. $config # load vdr configuration
+[ -f "$VDRCONFDIR/plugins/vdrsetup.conf" ] && . "$VDRCONFDIR/plugins/vdrsetup.conf"
+
+# x-vdr
+have_xvdr="on"
+[ ! -f /usr/local/src/x-vdr/setup.conf ] && have_xvdr="off"
+
+# /tmp/vdr-help.tmp
+[ ! -f /tmp/vdr-help.tmp ] && $VDRPRG --help | sed -e '/^$/d' -e 's/^. //' > /tmp/vdr-help.tmp
+
+# vdr-desc.cache
+[ ! -f /tmp/vdr-desc.cache ] && $VDRPRG --version | sed -e 's/\(^[^ ]*\)\(.*$\)/\1_desc="\1\2"/' -e '/^streamdev/s/-/_/' -e 's/^\[.*//' | sort > /tmp/vdr-desc.cache
+. /tmp/vdr-desc.cache
+vdr_version=$(echo $vdr_desc | sed -e 's/[^)]*$//')
+plugin_api=$(echo $vdr_desc | sed -e 's/[^)]*$//' -e 's/^[^\/]*//g' -e 's/\///' -e 's/\/*)//')
+
+# reset logfile
+[ "$vdrsetup_use_log" = "on" ] && echo "$0" > "$vdrsetup_log"
+
+# helper functions =>
+function DoLog() {
+ [ "$vdrsetup_use_log" = "on" ] && echo "$*" >> "$vdrsetup_log"
+ [ "$vdrsetup_use_screen" = "on" ] && echo ">> $*"
+ [ "$vdrsetup_use_syslog" = "on" ] && $LOGGER "$*"
+}
+
+function CreateDir() {
+ directory="$*"
+ if [ ! -d "$directory" ]; then
+ SendCmd "Message -seconds 10 'Create $directory?'" || return $false
+ if IsEvent Message keyOk ; then
+ mkdir -p "$directory"
+ chown -R $VDRUSER:$VDRGROUP "$directory"
+ [ -d "$directory" ] && DoLog "creating \"$directory\""
+ fi
+ else
+ DoLog "directory \"$directory\" already exist"
+ fi
+}
+
+# Menu functions =>
+
+##### Script-Setup Menu #####
+function ScriptSetup() {
+ # Menu --------------------------------------------------------------------------------------------------------------------------------->
+ SendCmd "enterlocal" || return $false
+ SendCmd "menu=New Menu 'Skript Intern'" || return $false
+ SendCmd "menu.SetColorKeyText -blue 'Help'" || return $false
+ SendCmd "menu.SetColumns 24" || return $false
+ SendCmd "menu.EnableEvent keyOk keyBlue close" || return $false
+ # List --------------------------------------------------------------------------------------------------------------------------------->
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Einstellungen ---'" || return $false
+ SendCmd "opt1=menu.AddNew EditListItem -selectname '$vdrsetup_ok_ends' 'Beim Speichern beenden' on off" || return $false
+ SendCmd "opt2=menu.AddNew EditIntItem 'Tasten Timeout (ms)' '$vdrsetup_key_timeout'" || return $false
+ SendCmd "menu.AddNew OsdItem -unselectable ''" || return $false
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Logging ---'" || return $false
+ SendCmd "opt3=menu.AddNew EditListItem -selectname '$vdrsetup_use_log' 'Benutze Log Datei' on off" || return $false
+ SendCmd "opt4=menu.AddNew EditStrItem 'Log Datei' $vdrsetup_log'" || return $false
+ SendCmd "opt5=menu.AddNew EditListItem -selectname '$vdrsetup_use_syslog' 'Syslog benutzen' on off" || return $false
+ SendCmd "opt6=menu.AddNew EditListItem -selectname '$vdrsetup_use_debug' 'Debug Nachrichten' on off" || return $false
+ SendCmd "menu.AddNew OsdItem -unselectable ''" || return $false
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Befehle ---'" || return $false
+ SendCmd "opt7=menu.AddNew OsdItem 'Cache neu erstellen'" || return $false
+ SendCmd "opt7.EnableEvent keyRed focus blur" || return $false
+ # <--------------------------------------------------------------------------------------------------------------------------------- List
+ SendCmd "_focus.addsubmenu menu" || return $false
+ SendCmd "opt1.SetCurrent" || return $false
+ SendCmd "menu.show" || return $false
+ # <--------------------------------------------------------------------------------------------------------------------------------- Menu
+
+ while true; do
+ SendCmd "menu.SleepEvent" || return $false
+ # keyOk
+ if IsEvent menu keyOk ; then
+ echo "# $script" > "$VDRCONFDIR/plugins/vdrsetup.conf"
+ # vdrsetup_ok_ends
+ SendCmd "opt1.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ vdrsetup_ok_ends="${reply6xx[1]}"
+ echo "vdrsetup_ok_ends=\"$vdrsetup_ok_ends\"" >> "$VDRCONFDIR/plugins/vdrsetup.conf"
+ # key_timeout
+ SendCmd "opt2.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ vdrsetup_key_timeout="${reply6xx[1]}"
+ echo "vdrsetup_key_timeout=\"$vdrsetup_key_timeout\"" >> "$VDRCONFDIR/plugins/vdrsetup.conf"
+ # vdrsetup_use_log
+ SendCmd "opt3.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ vdrsetup_use_log="${reply6xx[1]}"
+ echo "vdrsetup_use_log=\"$vdrsetup_use_log\"" >> "$VDRCONFDIR/plugins/vdrsetup.conf"
+ # vdrsetup_log
+ SendCmd "opt4.GetValue -quoted" || return $false
+ if [ "${reply5xx[0]}" = 500 ]; then
+ value=$(echo " ${reply5xx[1]}" | sed -e 's/^ //g' -e 's/\\r//g')
+ [ -d "$(dirname "$value")" ] && vdrsetup_log="$value"
+ fi
+ echo "vdrsetup_log=\"$vdrsetup_log\"" >> "$VDRCONFDIR/plugins/vdrsetup.conf"
+ # vdrsetup_use_syslog
+ SendCmd "opt5.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ vdrsetup_use_syslog="${reply6xx[1]}"
+ echo "vdrsetup_use_syslog=\"$vdrsetup_use_syslog\"" >> "$VDRCONFDIR/plugins/vdrsetup.conf"
+ # vdrsetup_use_debug
+ SendCmd "opt6.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ vdrsetup_use_debug="${reply6xx[1]}"
+ echo "vdrsetup_use_debug=\"$vdrsetup_use_debug\"" >> "$VDRCONFDIR/plugins/vdrsetup.conf"
+ # close the submenu
+ if [ "$vdrsetup_ok_ends" = "on" ]; then
+ SendCmd "menu.SendState osBack" || return $false
+# SendCmd "delete menu" || return $false
+# SendCmd "leavelocal" || return $false
+# return $true
+ fi
+ fi
+ if IsEvent opt7 focus ; then
+ SendCmd "menu.SetColorKeyText -red Create" || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ if IsEvent opt7 blur ; then
+ SendCmd "menu.SetColorKeyText -red ''" || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ if IsEvent opt7 keyRed ; then
+ SendCmd "Message -seconds 10 '/tmp/vdr-desc.cache erstellen?'" || return $false
+ if IsEvent Message keyOk ; then
+ $VDRPRG --version | sed -e 's/\(^[^ ]*\)\(.*$\)/\1_desc="\1\2"/' -e '/^streamdev/s/-/_/' -e 's/^\[.*//' | sort > /tmp/vdr-desc.cache
+ . /tmp/vdr-desc.cache
+ fi
+ fi
+ # keyBlue (Help)
+ if IsEvent menu keyBlue ; then
+ SendCmd "Message -seconds 5 'Auswahl mit Links und Rechts, speichern mit Ok'" || return $false
+ fi
+ # close the submenu
+ if IsEvent menu close ; then
+ SendCmd "delete menu" || return $false
+ SendCmd "leavelocal" || return $false
+ return $true
+ fi
+ done
+}
+
+##### System-Start Menu #####
+function SystemStart() {
+ Gdm="$(find /etc/rc*d/S*gdm)"
+ if [ -n "$Gdm" ]; then
+ DESKTOP_MANGER="gdm"
+ [ -f /etc/rc2.d/S30gdm ] && echo warning
+ [ -f /etc/rc3.d/S30gdm ] && DESKTOP="on"
+ fi
+
+ RUNLEVEL_STR="$(cat /etc/inittab | grep -m1 ^id:)"
+ RUNLEVEL=$(echo $RUNLEVEL_STR | cut -f2 -d":")
+ if [ $RUNLEVEL -eq 5 ] || [ "$DESKTOP_MANGER" ="gdm" -a $RUNLEVEL -gt 2 ]; then
+ DESKTOP="on"
+ else
+ DESKTOP="off"
+ fi
+
+ # Menu ------------------------------------------------------------------------------------------------------------->
+ SendCmd "enterlocal" || return $false
+ SendCmd "menu=New Menu 'Systemstart'" || return $false
+ SendCmd "menu.SetColorKeyText -blue 'Help'" || return $false
+ SendCmd "menu.SetColumns 24" || return $false
+ SendCmd "menu.EnableEvent keyOk close" || return $false
+ # List ------------------------------------------------------------------------------------------------------------->
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Programme ---'" || return $false
+ SendCmd "opt1=menu.AddNew EditListItem -selectname '$AUTOSTART' 'VDR' on off" || return $false
+ SendCmd "opt1.EnableEvent keyBlue" || return $false
+ SendCmd "opt2=menu.AddNew EditListItem -selectname '$XPLAYER' 'Xserver und Player' on off" || return $false
+ SendCmd "opt2.EnableEvent keyBlue" || return $false
+ SendCmd "opt3=menu.AddNew EditListItem -selectname '$DESKTOP' 'Desktop-Manager' on off" || return $false
+ SendCmd "opt3.EnableEvent keyBlue" || return $false
+ SendCmd "opt4=menu.AddNew EditListItem -selectname '$vdradmin' 'VDR-Admin' on off" || return $false
+ SendCmd "opt4.EnableEvent keyBlue" || return $false
+ SendCmd "menu.AddNew OsdItem -unselectable ''" || return $false
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Module ---'" || return $false
+ SendCmd "optA=menu.AddNew EditListItem -selectname '$em8300' 'DXR3' on off" || return $false
+ SendCmd "optA.EnableEvent keyBlue" || return $false
+ SendCmd "optB=menu.AddNew EditListItem -selectname '$graphlcd_base' 'GraphLCD' on off" || return $false
+ SendCmd "optB.EnableEvent keyBlue" || return $false
+ SendCmd "optC=menu.AddNew EditListItem -selectname '$LCDproc' 'LCDproc' on off" || return $false
+ SendCmd "optC.EnableEvent keyBlue" || return $false
+ SendCmd "optD=menu.AddNew EditListItem -selectname '$lirc' 'Lirc' on off" || return $false
+ SendCmd "optD.EnableEvent keyBlue" || return $false
+ SendCmd "optE=menu.AddNew EditIntItem -min 1 -max 2 'Lirc Port' '$lirc_port'" || return $false
+ SendCmd "optE.EnableEvent keyBlue" || return $false
+ # <------------------------------------------------------------------------------------------------------------- List
+ SendCmd "_focus.addsubmenu menu" || return $false
+ SendCmd "opt1.SetCurrent" || return $false
+ SendCmd "menu.show" || return $false
+ # <------------------------------------------------------------------------------------------------------------- Menu
+
+ while true; do
+ SendCmd "menu.SleepEvent" || return $false
+ # keyOk
+ if IsEvent menu keyOk ; then
+ # AUTOSTART
+ SendCmd "opt1.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$AUTOSTART" != "${reply6xx[1]}" ]; then
+ DoLog "changing AUTOSTART to ${reply6xx[1]}"
+ sed -i $config -e "/^AUTOSTART=/s/$AUTOSTART/${reply6xx[1]}/"
+ AUTOSTART=${reply6xx[1]}
+ fi
+ # Xserver and Player
+ SendCmd "opt2.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$XPLAYER" != "${reply6xx[1]}" ]; then
+ DoLog "changing XPLAYER to ${reply6xx[1]}"
+ sed -i $config -e "/^XPLAYER=/s/$XPLAYER/${reply6xx[1]}/"
+ XPLAYER=${reply6xx[1]}
+ fi
+ # Desktop-Manager
+ SendCmd "opt3.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$DESKTOP" != "${reply6xx[1]}" ] || [ "$DESKTOP" = "$XPLAYER" ]; then
+ DESKTOP=${reply6xx[1]}
+ [ "$XPLAYER" = "on" ] && DESKTOP="off"
+ DoLog "changing Desktop-Manager to $DESKTOP"
+ # runlevel
+ if [ $RUNLEVEL -eq 5 -a "$DESKTOP" = "off" ]; then
+ sed -i /etc/inittab -e "s/$RUNLEVEL_STR/id:3:initdefault:/"
+ elif [ $RUNLEVEL -ne 5 -a "$DESKTOP" = "on" ]; then
+ sed -i /etc/inittab -e "s/$RUNLEVEL_STR/id:5:initdefault:/"
+ fi
+ XWRAPPER_STR="$(cat /etc/X11/Xwrapper.config | grep -m1 ^allowed_users)"
+ XWRAPPER=$(echo $XWRAPPER_STR | cut -f2 -d"=")
+ if [ "$DESKTOP" != "on" -a "$XWRAPPER" != "anybody" ]; then
+ log "Setting 'allowed_users' to 'anybody' in /etc/X11/Xwrapper.config"
+ sed -i /etc/X11/Xwrapper.config -e "s/$XWRAPPER_STR/allowed_users=anybody/g"
+ fi
+ fi
+ # vdradmin
+ SendCmd "opt4.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$vdradmin" != "${reply6xx[1]}" ]; then
+ DoLog "changing vdradmin to ${reply6xx[1]}"
+ sed -i $config -e "/^vdradmin=/s/$vdradmin/${reply6xx[1]}/"
+ vdradmin=${reply6xx[1]}
+ fi
+ # dxr3
+ SendCmd "optA.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$em8300" != "${reply6xx[1]}" ]; then
+ DoLog "changing em8300 to ${reply6xx[1]}"
+ sed -i $config -e "/^em8300=/s/$em8300/${reply6xx[1]}/"
+ em8300=${reply6xx[1]}
+ fi
+ # graphlcd_base
+ SendCmd "optB.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$graphlcd_base" != "${reply6xx[1]}" ]; then
+ DoLog "changing graphlcd_base to ${reply6xx[1]}"
+ sed -i $config -e "/^graphlcd_base=/s/$graphlcd_base/${reply6xx[1]}/"
+ graphlcd_base=${reply6xx[1]}
+ fi
+ # LCDproc
+ SendCmd "optC.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$LCDproc" != "${reply6xx[1]}" ]; then
+ DoLog "changing LCDproc to ${reply6xx[1]}"
+ sed -i $config -e "/^LCDproc=/s/$LCDproc/${reply6xx[1]}/"
+ LCDproc=${reply6xx[1]}
+ fi
+ # lirc
+ SendCmd "optD.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$lirc" != "${reply6xx[1]}" ]; then
+ DoLog "changing lirc to ${reply6xx[1]}"
+ sed -i $config -e "/^lirc=/s/$lirc/${reply6xx[1]}/"
+ lirc=${reply6xx[1]}
+ fi
+ # lirc_port
+ SendCmd "optE.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$lirc_port" != "${reply6xx[1]}" ]; then
+ DoLog "changing lirc_port to ${reply6xx[1]}"
+ sed -i $config -e "/^lirc_port=/s/$lirc_port/${reply6xx[1]}/"
+ lirc_port=${reply6xx[1]}
+ fi
+ # close the submenu
+ if [ "$vdrsetup_ok_ends" = "on" ]; then
+ SendCmd "menu.SendState osBack" || return $false
+# SendCmd "delete menu" || return $false
+# SendCmd "leavelocal" || return $false
+# return $true
+ fi
+ fi
+
+ # keyBlue (Help)
+ if IsEvent opt1 keyBlue ; then
+ SendCmd "Message -seconds 5 'VDR automatisch starten on/off'" || return $false
+ fi
+ if IsEvent opt2 keyBlue ; then
+ SendCmd "Message -seconds 5 'Xserver und Player automatisch starten on/off'" || return $false
+ fi
+ if IsEvent opt3 keyBlue ; then
+ SendCmd "Message -seconds 5 'Desktop-Manager automatisch starten on/off'" || return $false
+ fi
+ if IsEvent opt4 keyBlue ; then
+ SendCmd "Message -seconds 5 'VDRAdmin automatisch starten on/off'" || return $false
+ fi
+
+ if IsEvent optA keyBlue ; then
+ SendCmd "Message -seconds 5 'Modul automatisch laden on/off'" || return $false
+ fi
+ if IsEvent optB keyBlue ; then
+ SendCmd "Message -seconds 5 'Modul automatisch laden on/off'" || return $false
+ fi
+ if IsEvent optC keyBlue ; then
+ SendCmd "Message -seconds 5 'Modul automatisch laden on/off'" || return $false
+ fi
+ if IsEvent optD keyBlue ; then
+ SendCmd "Message -seconds 5 'Modul automatisch laden on/off'" || return $false
+ fi
+ if IsEvent optE keyBlue ; then
+ SendCmd "Message -seconds 5 'Lirc-Port 1/2'" || return $false
+ fi
+ # close the submenu
+ if IsEvent menu close ; then
+ SendCmd "delete menu" || return $false
+ SendCmd "leavelocal" || return $false
+ return $true
+ fi
+ done
+}
+
+##### VDR-Setup Menu #####
+function VdrSetup() {
+ # directorys
+ videodir=$( grep "^VIDEODIR=" $config | tail -n1 | sed -e "s/^VIDEODIR=//" -e 's/"//g' -e 's/#.*//')
+ musicdir=$( grep "^MUSICDIR=" $config | tail -n1 | sed -e "s/^MUSICDIR=//" -e 's/"//g' -e 's/#.*//')
+ dvdisodir=$( grep "^DVDISODIR=" $config | tail -n1 | sed -e "s/^DVDISODIR=//" -e 's/"//g' -e 's/#.*//')
+ picturedir=$(grep "^PICTUREDIR=" $config | tail -n1 | sed -e "s/^PICTUREDIR=//" -e 's/"//g' -e 's/#.*//')
+ divxdir=$( grep "^DIVXDIR=" $config | tail -n1 | sed -e "s/^DIVXDIR=//" -e 's/"//g' -e 's/#.*//')
+ vdrvardir=$( grep "^VDRVARDIR=" $config | tail -n1 | sed -e "s/^VDRVARDIR=//" -e 's/"//g' -e 's/#.*//')
+ dvdburner=$( grep "^DVDBURNER=" $config | tail -n1 | sed -e "s/^DVDBURNER=//" -e 's/"//g' -e 's/#.*//')
+
+ ls_dev=$(ls -r /dev/*)
+
+ # cd / dvd
+ all_dvd=$(echo "$ls_dev" | grep "^/dev/[c,d][d,v][r,d]" | sort -r)
+ if [ -n "$all_dvd" ] && [ $(echo $all_dvd | grep -cw "$dvdburner") -eq 0 ]; then
+ dvdburner_old="$dvdburner"
+ dvdburner=$(echo $all_dvd | cut -f 1 -d " ")
+ sed -i $config -e "/^DVDBURNER=/s?$dvdburner_old?$dvdburner?"
+ SendCmd "Message -seconds 5 \"Resetting DVDBURNER to $dvdburner\"" || return $false
+ fi
+
+ # vdr keyb tty
+ all_tty=$(echo "$ls_dev" | grep "^/dev/tty[0-9]$")
+ if [ $(echo $all_tty | grep -cw "$KEYB_TTY") -eq 0 ]; then
+ KEYB_TTY_OLD="$KEYB_TTY"
+ KEYB_TTY=""
+ sed -i $config -e "/^KEYB_TTY=/s/$KEYB_TTY_OLD/$KEYB_TTY/"
+ SendCmd "Message -seconds 5 \"Resetting KEYB_TTY to $KEYB_TTY\"" || return $false
+ fi
+
+ # vdr language
+ all_lang=$(locale -a | grep "_")
+ if [ $(echo $all_lang | grep -cw "$VDRLANG") -eq 0 ]; then
+ VDRLANG_OLD="$VDRLANG"
+ VDRLANG=$(echo $VDRLANG | cut -f 1 -d ".")
+ [ $(echo $all_lang | grep -cw "$VDRLANG") -eq 0 ] && VDRLANG=$(echo $all_lang | cut -f 1 -d " ")
+ sed -i $config -e "/^VDRLANG=/s/$VDRLANG_OLD/$VDRLANG/"
+ SendCmd "Message -seconds 5 \"Resetting VDRLANG to $VDRLANG\"" || return $false
+ fi
+
+ # Menu -------------------------------------------------------------------------------------------------------------->
+ SendCmd "enterlocal" || return $false
+ SendCmd "menu=New Menu '$vdr_version'" || return $false
+ SendCmd "menu.SetColorKeyText -blue 'Help'" || return $false
+ SendCmd "menu.SetColumns 24" || return $false
+ SendCmd "menu.EnableEvent keyOk keyBlue close" || return $false
+ # List -------------------------------------------------------------------------------------------------------------->
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Einstellungen ---'" || return $false
+ SendCmd "opt1=menu.AddNew EditListItem -selectname '$noad' 'Schnittmarken erkennen' on off" || return $false
+ SendCmd "opt2=menu.AddNew EditListItem -selectname '$KEYB_TTY' 'KEYB TTY' \"\" $(echo $all_tty)" || return $false
+ SendCmd "opt3=menu.AddNew EditListItem -selectname '$VDRLANG' 'Sprache (LANG)' $(echo $all_lang)" || return $false
+ SendCmd "opt4=menu.AddNew EditIntItem 'Watchdog Timeout (sec)' '$WATCHDOG'" || return $false
+ SendCmd "opt5=menu.AddNew EditIntItem 'XV Display' '$XV_DISPLAY'" || return $false
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Laufwerke & Verzeichnisse ---'" || return $false
+ SendCmd "optA=menu.AddNew EditStrItem 'Video' '$videodir'" || return $false
+ SendCmd "optB=menu.AddNew EditStrItem 'Musik' '$musicdir'" || return $false
+ SendCmd "optC=menu.AddNew EditStrItem 'DVD-Isos' '$dvdisodir'" || return $false
+ SendCmd "optD=menu.AddNew EditStrItem 'Bilder und Fotos' '$picturedir'" || return $false
+ SendCmd "optE=menu.AddNew EditStrItem 'DivX und Co' '$divxdir'" || return $false
+ SendCmd "optF=menu.AddNew EditStrItem 'EPG Daten' '$vdrvardir'" || return $false
+ SendCmd "optG=menu.AddNew EditListItem -selectname '$dvdburner' 'DVD Brenner' \"\" $(echo $all_dvd)" || return $false
+ # <-------------------------------------------------------------------------------------------------------------- List
+ SendCmd "_focus.addsubmenu menu" || return $false
+ SendCmd "opt1.SetCurrent" || return $false
+ SendCmd "menu.show" || return $false
+ # <-------------------------------------------------------------------------------------------------------------- Menu
+
+ while true; do
+ SendCmd "menu.SleepEvent" || return $false
+ # keyOk
+ if IsEvent menu keyOk ; then
+ # noad
+ SendCmd "opt1.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ value="${reply6xx[1]}"
+ if [ "$noad" != "$value" ]; then
+ DoLog "changing noad to $value"
+ sed -i $config -e "/^noad=/s/$noad/$value/"
+ noad="$value"
+ fi
+ # KEYB_TTY
+ SendCmd "opt2.GetValue -name" || return $false
+ if [ "${reply6xx[0]}" = 600 ]; then
+ value="${reply6xx[1]}"
+ elif [ "${reply2xx[0]}" = 200 ]; then
+ value=""
+ else
+ return $false
+ fi
+ if [ "$KEYB_TTY" != "$value" ]; then
+ DoLog "changing KEYB_TTY to \"$value\""
+ sed -i $config -e "/^KEYB_TTY=/s/\"//g" -e "s?^KEYB_TTY=$KEYB_TTY?KEYB_TTY=\"$value\"?"
+ KEYB_TTY="$value"
+ fi
+
+ # VDRLANG
+ SendCmd "opt3.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ value="${reply6xx[1]}"
+ if [ "$VDRLANG" != "$value" ]; then
+ DoLog "changing VDRLANG to $value"
+ sed -i $config -e "/^VDRLANG=/s/$VDRLANG/$value/"
+ VDRLANG="$value"
+ fi
+
+ # WATCHDOG
+ SendCmd "opt4.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ value="${reply6xx[1]}"
+ if [ "$WATCHDOG" != "$value" ]; then
+ DoLog "changing WATCHDOG to $value"
+ sed -i $config -e "/^WATCHDOG=/s/$WATCHDOG/$value/"
+ WATCHDOG="$value"
+ fi
+ # XV_DISPLAY
+ SendCmd "opt5.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ value="${reply6xx[1]}"
+ if [ "$XV_DISPLAY" != "$value" ]; then
+ DoLog "changing XV_DISPLAY to $value"
+ sed -i $config -e "/^XV_DISPLAY=/s/$XV_DISPLAY/$value/"
+ XV_DISPLAY="$value"
+ fi
+ # VIDEODIR
+ SendCmd "optA.GetValue -quoted" || return $false
+ [ "${reply5xx[0]}" != 500 ] && return $false
+ value="${reply5xx[1]}"
+ if [ "$videodir" != "$value" ]; then
+ [ ! -d "$value" ] && CreateDir "$value"
+ if [ -d "$value" ]; then
+ DoLog "changing VIDEODIR to $value"
+ sed -i $config -e "/^VIDEODIR=/s?$videodir?$value?"
+ else
+ DoLog "error: directory \"$value\" does not exists"
+ fi
+ fi
+ # MUSICDIR
+ SendCmd "optB.GetValue -quoted" || return $false
+ [ "${reply5xx[0]}" != 500 ] && return $false
+ value="${reply5xx[1]}"
+ if [ "$musicdir" != "$value" ]; then
+ [ ! -d "$value" ] && CreateDir "$value"
+ if [ -d "$value" ]; then
+ DoLog "changing MUSICDIR to $value"
+ sed -i $config -e "/^MUSICDIR=/s?$musicdir?$value?"
+ else
+ DoLog "error: directory \"$value\" does not exists"
+ fi
+ fi
+ # DVDISODIR
+ SendCmd "optC.GetValue -quoted" || return $false
+ [ "${reply5xx[0]}" != 500 ] && return $false
+ value="${reply5xx[1]}"
+ if [ "$dvdisodir" != "$value" ]; then
+ [ ! -d "$value" ] && CreateDir "$value"
+ if [ -d "$value" ]; then
+ DoLog "changing DVDISODIR to $value"
+ sed -i $config -e "/^DVDISODIR=/s?$dvdisodir?$value?"
+ else
+ DoLog "error: directory \"$value\" does not exists"
+ fi
+ fi
+ # PICTUREDIR
+ SendCmd "optD.GetValue -quoted" || return $false
+ [ "${reply5xx[0]}" != 500 ] && return $false
+ value="${reply5xx[1]}"
+ if [ "$picturedir" != "$value" ]; then
+ [ ! -d "$value" ] && CreateDir "$value"
+ if [ -d "$value" ]; then
+ DoLog "changing PICTUREDIR to $value"
+ sed -i $config -e "/^PICTUREDIR=/s?$picturedir?$value?"
+ else
+ DoLog "error: directory \"$value\" does not exists"
+ fi
+ fi
+ # DIVXDIR
+ SendCmd "optE.GetValue -quoted" || return $false
+ [ "${reply5xx[0]}" != 500 ] && return $false
+ value="${reply5xx[1]}"
+ if [ "$divxdir" != "$value" ]; then
+ [ ! -d "$value" ] && CreateDir "$value"
+ if [ -d "$value" ]; then
+ DoLog "changing DIVXDIR to $value"
+ sed -i $config -e "/^DIVXDIR=/s?$divxdir?$value?"
+ else
+ DoLog "error: directory \"$value\" does not exists"
+ fi
+ fi
+ # VDRVARDIR
+ SendCmd "optF.GetValue -quoted" || return $false
+ [ "${reply5xx[0]}" != 500 ] && return $false
+ value="${reply5xx[1]}"
+ if [ "$vdrvardir" != "$value" ]; then
+ [ ! -d "$value" ] && CreateDir "$value"
+ if [ -d "$value" ]; then
+ DoLog "changing VDRVARDIR to $value"
+ sed -i $config -e "/^VDRVARDIR=/s?$vdrvardir?$value?"
+ else
+ DoLog "error: directory \"$value\" does not exists"
+ fi
+ fi
+ # DVDBURNER
+ SendCmd "optG.GetValue -name" || return $false
+ if [ "${reply6xx[0]}" = 600 ]; then
+ value="${reply6xx[1]}"
+ elif [ "${reply2xx[0]}" = 200 ]; then
+ value=""
+ else
+ return $false
+ fi
+ if [ "$dvdburner" != "$value" ]; then
+ DoLog "changing DVDBURNER to \"$value\""
+ [ ! -e "$value" ] && DoLog "warning: \"$value\" is no valid"
+# sed -i $config -e "/^DVDBURNER=/s?$dvdburner?$value?"
+ sed -i $config -e "/^DVDBURNER=/s/\"//g" -e "s?^DVDBURNER=$dvdburner?DVDBURNER=\"$value\"?"
+ fi
+ # close the submenu
+ if [ "$vdrsetup_ok_ends" = "on" ]; then
+ SendCmd "menu.SendState osBack" || return $false
+# SendCmd "delete menu" || return $false
+# SendCmd "leavelocal" || return $false
+# return $true
+ fi
+ fi
+ # keyBlue (Help)
+ if IsEvent menu keyBlue ; then
+ SendCmd "Message -seconds 5 'Speichern mit Ok oder Menue verlassen mit Exit'" || return $false
+ fi
+ # close the submenu
+ if IsEvent menu close ; then
+ SendCmd "delete menu" || return $false
+ SendCmd "leavelocal" || return $false
+ return $true
+ fi
+ done
+}
+
+##### Plugin Menus #####
+function PluginSetup() {
+ plugin=$1
+ plugin_have_args=$(grep "^${plugin}_args=" $config | tail -n1)
+ plugin_version=$(grep "^${plugin}_desc=" /tmp/vdr-desc.cache | tail -n1 | sed -e "s/^${plugin}_desc=//" -e 's/[^)]*$//' -e 's/"//g' -e 's/#.*//')
+ [ -n "$plugin_version" ] || plugin_version="$plugin"
+ # Menu -------------------------------------------------------------------------------------------------------------------->
+ SendCmd "enterlocal" || return $false
+ SendCmd "menu=New Menu '$plugin_version'" || return $false
+ SendCmd "menu.SetColorKeyText -green 'About' -blue 'Help'" || return $false
+ SendCmd "menu.SetColumns 14" || return $false
+ SendCmd "menu.EnableEvent keyOk keyGreen keyBlue close" || return $false
+ # List -------------------------------------------------------------------------------------------------------------------->
+ SendCmd "opt1=menu.AddNew EditListItem -selectname '${!plugin}' 'Status' on off" || return $false
+ if [ "$plugin" = "remote" ]; then
+ SendCmd "optA=menu.AddNew EditStrItem 'Remote Event' '$remote_event'" || return $false
+ fi
+ if [ -n "$plugin_have_args" ]; then
+ plugin_args=$(echo "$plugin_have_args" | sed -e "s/^${plugin}_args=//" -e 's/"//g' -e 's/#.*//')
+ SendCmd "opt2=menu.AddNew EditStrItem 'Argumente' '$plugin_args'" || return $false
+ else
+ SendCmd "menu.AddNew OsdItem -unselectable 'Argumente:\tPlugin kennt keine Argumente'" || return $false
+ fi
+ # <-------------------------------------------------------------------------------------------------------------------- List
+ SendCmd "_focus.addsubmenu menu" || return $false
+ SendCmd "opt1.SetCurrent" || return $false
+ SendCmd "menu.show" || return $false
+ # <-------------------------------------------------------------------------------------------------------------------- Menu
+
+ while true; do
+ SendCmd "menu.SleepEvent" || return $false
+ # keyOk
+ if IsEvent menu keyOk ; then
+ # plugin status
+ SendCmd "opt1.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ value="${reply6xx[1]}"
+ if [ "${!plugin}" != "$value" ]; then
+ DoLog "changing $plugin to $value"
+ sed -i $config -e "/^$plugin=/s/${!plugin}/$value/"
+ eval $plugin="$value"
+ fi
+ # plugin arguments
+ if [ -n "$plugin_have_args" ]; then
+ SendCmd "opt2.GetValue -quoted" || return $false
+ if [ "${reply5xx[0]}" = 500 ] || [ "${reply2xx[0]}" = 200 ]; then
+ value=$(echo " ${reply5xx[1]}" | sed -e 's/^ //g' -e 's/\\r//g')
+ if [ "$plugin_args" != "$value" ]; then
+ DoLog "changing arguments for $plugin to $value"
+ sed -i $config -e "/^${plugin}_args=/s/\"//g" -e "s?^${plugin}_args=$plugin_args?${plugin}_args=\"$value\"?"
+ fi
+ fi
+ fi
+ # remote_event for remote plugin
+ if [ "$plugin" = "remote" ]; then
+ SendCmd "optA.GetValue -quoted" || return $false
+ [ "${reply5xx[0]}" = 500 ] || return $false
+ value=$(echo " ${reply5xx[1]}" | sed -e 's/^ //g' -e 's/\\r//g')
+ if [ "$remote_event" != "$value" ]; then
+ DoLog "changing arguments for remote_event to $value"
+ sed -i $config -e "/^remote_event=/s?$remote_event?$value?"
+ fi
+ fi
+ # close the submenu
+ if [ "$vdrsetup_ok_ends" = "on" ]; then
+ SendCmd "menu.SendState osBack" || return $false
+# SendCmd "delete menu" || return $false
+# SendCmd "leavelocal" || return $false
+# return $true
+ fi
+ fi
+ # keyGreen (About)
+ if IsEvent menu keyGreen ; then
+ desc=$(grep "^${plugin}_desc=" /tmp/vdr-desc.cache | tail -n1 | sed -e "s/^${plugin}_desc=\"${plugin} ([^)]*) - //" -e 's/"//g' -e 's/#.*//')
+ SendCmd "Message -seconds 5 \"$desc\"" || return $false
+ fi
+ # keyBlue (Help)
+ if IsEvent menu keyBlue ; then
+ SendCmd "Message -seconds 5 'Auswahl mit Links und Rechts, speichern mit Ok'" || return $false
+ fi
+ # close the submenu
+ if IsEvent menu close ; then
+ SendCmd "delete menu" || return $false
+ SendCmd "leavelocal" || return $false
+ return $true
+ fi
+ done
+}
+
+function PluginStatusList() {
+ # Menu ---------------------------------------------------------------------------------------------------->
+ SendCmd "enterlocal" || return $false
+ SendCmd "menu=New Menu 'Plugin Status Liste'" || return $false
+ SendCmd "menu.SetColorKeyText -green 'About' -blue 'Help'" || return $false # -red 'Edit'
+ SendCmd "menu.SetColumns 24" || return $false
+ SendCmd "menu.EnableEvent keyOk keyGreen keyBlue close" || return $false # keyRed
+ # List ---------------------------------------------------------------------------------------------------->
+ SendCmd "Message -status 'Menu wird aufgebaut ...'" || return $false
+ files=$(ls "$VDRLIBDIR" | grep "^libvdr-" | grep "${plugin_api}$" | sed -e "s/^libvdr-//g" -e "s/.so.${plugin_api}$//g")
+ if [ -n "$files" ]; then
+ for p in $files ; do
+ plugin=$(echo $p | sed -e "s/-/_/g") # this is for streamdev-server ...
+ if [ -z "${!plugin}" ]; then
+ DoLog "creating $plugin=\"off\""
+ echo "" >> $config
+ echo "# $plugin" >> $config
+ echo "${plugin}=\"off\"" >> $config
+ eval ${plugin}="off"
+ # plugin args
+ nl=0; (( nl=$(grep -n "^$p " /tmp/vdr-help.tmp | cut -f 1 -d ":") +1 ))
+ [ $nl -gt 1 ] && [ $(sed -n "${nl}p" /tmp/vdr-help.tmp | grep -c "^-") -gt 0 ] && echo "${plugin}_args=\"\"" >> $config
+ fi
+ SendCmd "$plugin=menu.AddNew EditListItem -selectname \"${!plugin}\" \"$p\" on off" || return $false
+ done
+ fi
+ SendCmd "Message -statusclear" || return $false
+ # <---------------------------------------------------------------------------------------------------- List
+ SendCmd "_focus.addsubmenu menu" || return $false
+ SendCmd "menu.Show" || return $false
+ # <---------------------------------------------------------------------------------------------------- Menu
+
+ while true ; do
+ SendCmd "menu.SleepEvent" || return $false
+ # keyOk (save settings and clsoe the submenu)
+ if IsEvent menu keyOk ; then
+ if [ -n "$files" ]; then
+ SendCmd "Message 'Einstellungen werden gespeichert'" || return $false
+ for p in $files ; do
+ plugin=$(echo $p | sed -e "s/-/_/g") # this is for streamdev-server ...
+ SendCmd "${plugin}.GetValue -name" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "${!plugin}" != "${reply6xx[1]}" ]; then
+ DoLog "changing $plugin to ${reply6xx[1]}"
+ sed -i $config -e "/^${plugin}=/s/${!plugin}/${reply6xx[1]}/"
+ eval ${plugin}="${reply6xx[1]}"
+ fi
+ done
+ fi
+ # close the submenu
+ if [ "$vdrsetup_ok_ends" = "on" ]; then
+ SendCmd "menu.SendState osBack" || return $false
+# SendCmd "delete menu" || return $false
+# SendCmd "leavelocal" || return $false
+# return $true
+ fi
+ fi
+ # keyRed (Edit) !!! Bug Inside !!!
+ if IsEvent menu keyRed ; then
+ SendCmd "menu.GetCurrent"
+ [ "${reply3xx[0]}" != 302 ] && return $false
+ PluginSetup "${reply3xx[2]}" || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyGreen (About)
+ if IsEvent menu keyGreen ; then
+ SendCmd "menu.GetCurrent"
+ [ "${reply3xx[0]}" != 302 ] && return $false
+ desc=$(grep "^${reply3xx[2]}_desc=" /tmp/vdr-desc.cache | tail -n1 | sed -e "s/^${reply3xx[2]}_desc=\"${reply3xx[2]} ([^)]*) - //" -e 's/"//g' -e 's/#.*//')
+ SendCmd "Message -seconds 5 \"$desc\"" || return $false
+ fi
+ # keyBlue (Help)
+ if IsEvent menu keyBlue ; then
+ SendCmd "Message -seconds 5 'Auswahl mit Links und Rechts, speichern mit Ok'" || return $false
+ fi
+ # close the submenu
+ if IsEvent menu close ; then
+ SendCmd "delete menu" || return $false
+ SendCmd "leavelocal" || return $false
+ return $true
+ fi
+ done
+}
+
+function ListPlugins() {
+ # Menu ------------------------------------------------------------------------------------------>
+ SendCmd "enterlocal" || return $false
+ SendCmd "menu=New Menu 'Installierte Plugins'" || return $false
+ SendCmd "menu.SetColorKeyText -red 'Edit' -green 'Status' -blue 'Help'" || return $false
+ SendCmd "menu.SetColumns 4" || return $false
+ SendCmd "menu.EnableEvent keyOk keyRed keyGreen keyBlue close" || return $false
+ SendCmd "menu.EnableEvent key1 key2 key3 key4 key5 key6 key7 key8 key9 key0" || return $false
+ # List------------------------------------------------------------------------------------------->
+ SendCmd "Message -status 'Menu wird aufgebaut ...'" || return $false
+ files=$(ls "$VDRLIBDIR" | grep "^libvdr-" | grep "${plugin_api}$" | sed -e "s/^libvdr-//g" -e "s/.so.${plugin_api}$//g")
+ if [ -n "$files" ]; then
+ n=0
+ for p in $files ; do
+ ((n=n+1))
+ plugin=$(echo $p | sed -e "s/-/_/g") # this is for streamdev-server ...
+ if [ -z "${!plugin}" ]; then
+ DoLog "creating $plugin=\"off\""
+ echo "" >> $config
+ echo "# $plugin" >> $config
+ echo "${plugin}=\"off\"" >> $config
+ eval ${plugin}="off"
+ # plugin args
+ nl=0; (( nl=$(grep -n "^$p " /tmp/vdr-help.tmp | cut -f 1 -d ":") +1 ))
+ [ $nl -gt 1 ] && [ $(sed -n "${nl}p" /tmp/vdr-help.tmp | grep -c "^-") -gt 0 ] && echo "${plugin}_args=\"\"" >> $config
+ fi
+ plugin_desc="${plugin}_desc"
+ desc="${!plugin_desc}"
+ [ -n "$(echo $n | grep "^[0-9][0-9][0-9]$")" ] && N="$n"
+ [ -n "$(echo $n | grep "^[0-9][0-9]$")" ] && N=" $n"
+ [ -n "$(echo $n | grep "^[0-9]$")" ] && N=" $n"
+ SendCmd "$plugin=menu.AddNew OsdItem \"$N \t$desc\"" || return $false
+ done
+ fi
+ SendCmd "Message -statusclear" || return $false
+ # <------------------------------------------------------------------------------------------ List
+ SendCmd "_focus.addsubmenu menu" || return $false
+ SendCmd "menu.Show" || return $false
+ # <------------------------------------------------------------------------------------------ Menu
+
+ while true ; do
+ SendCmd "menu.SleepEvent" || return $false
+ # keyOk and keyRed
+ if IsEvent menu keyOk || IsEvent menu keyRed; then
+ SendCmd "menu.GetCurrent"
+ [ "${reply3xx[0]}" != 302 ] && return $false
+ PluginSetup "${reply3xx[2]}" || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyGreen (Status)
+ if IsEvent menu keyGreen ; then
+ SendCmd "menu.GetCurrent"
+ [ "${reply3xx[0]}" != 302 ] && return $false
+ plugin="${reply3xx[2]}"
+ SendCmd "Message -seconds 5 'Status: ${!plugin}'" || return $false
+ fi
+ # keyBlue (Help)
+ if IsEvent menu keyBlue ; then
+ SendCmd "Message -seconds 5 'Auswahl mit OK oder Menue verlassen mit Exit'" || return $false
+ fi
+ # close the submenu
+ if IsEvent menu close ; then
+ SendCmd "delete menu" || return $false
+ SendCmd "leavelocal" || return $false
+ return $true
+ fi
+ # keyNr
+ if [ $n -gt 0 ]; then
+ mykey=;
+ k1=;
+ for k1 in 0 1 2 3 4 5 6 7 8 9 ; do
+ if IsEvent menu key$k1 ; then
+ mykey=$k1
+ [ $n -gt 9 ] || break
+ SendCmd "menu.SleepEvent -timeoutms $vdrsetup_key_timeout" || return $false
+ if IsEvent menu keyOk ; then break; fi
+ k2=;
+ for k2 in 0 1 2 3 4 5 6 7 8 9 ; do
+ if IsEvent menu key$k2 ; then
+ mykey=${k1}${k2}
+ [ $n -gt 99 ] || break
+ SendCmd "menu.SleepEvent -timeoutms $vdrsetup_key_timeout" || return $false
+ if IsEvent menu keyOk ; then break; fi
+ k3=;
+ for k3 in 0 1 2 3 4 5 6 7 8 9 ; do
+ if IsEvent menu key$k3 ; then
+ mykey=${k1}${k2}${k3}
+ break
+ fi
+ done
+ break
+ fi
+ done
+ break
+ fi
+ done
+
+ if [ -n "$mykey" ]; then
+ [ $mykey -gt $n ] && mykey=$n
+ ((mykey=mykey-1)) # we do this, because 0 is the first item
+ [ $mykey -lt 0 ] && mykey=0
+ SendCmd "menu.SetCurrent $mykey" || return $false
+# SendCmd "menu.GetCurrent"
+# [ "${reply3xx[0]}" != 302 ] && return $false
+# PluginSetup "${reply3xx[2]}" || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ fi
+ done
+}
+
+##### Wakeup Menu #####
+function Wakeup() {
+ shutdown="$VDRCONFDIR/shutdown.conf"
+ . $shutdown
+
+ # settings
+# WAKEUP_MODE=$(grep "^WAKEUP_MODE=" $shutdown | tail -n1 | sed -e "s/^WAKEUP_MODE=//" -e 's/"//g' -e 's/#.*//')
+ (( WAKEUP_MODE_T = $WAKEUP_MODE + 1 ))
+# NVRAM_CONFIG=$(grep "^NVRAM_CONFIG=" $shutdown | tail -n1 | sed -e "s/^NVRAM_CONFIG=//" -e 's/"//g' -e 's/#.*//')
+# NVRAM_IWNAME=$(grep "^NVRAM_IWNAME=" $shutdown | tail -n1 | sed -e "s/^NVRAM_IWNAME=//" -e 's/"//g' -e 's/#.*//')
+# NVRAM_OPT=$(grep "^NVRAM_OPT=" $shutdown | tail -n1 | sed -e "s/^NVRAM_OPT=//" -e 's/"//g' -e 's/#.*//')
+# NEED_REBOOT=$(grep "^NEED_REBOOT=" $shutdown | tail -n1 | sed -e "s/^NEED_REBOOT=//" -e 's/"//g' -e 's/#.*//')
+ (( NEED_REBOOT_T = $NEED_REBOOT + 1 ))
+# LILO=$(grep "^LILO=" $shutdown | tail -n1 | sed -e "s/^LILO=//" -e 's/"//g' -e 's/#.*//')
+ (( LILO_T = $LILO + 1 ))
+
+# WAKEUP_BEFORE=$(grep "^WAKEUP_BEFORE=" $shutdown | tail -n1 | sed -e "s/^WAKEUP_BEFORE=//" -e 's/"//g' -e 's/#.*//')
+# MAX_POWEROFF_TIME=$(grep "^MAX_POWEROFF_TIME=" $shutdown | tail -n1 | sed -e "s/^MAX_POWEROFF_TIME=//" -e 's/"//g' -e 's/#.*//')
+# INTERVAL=$(grep "^INTERVAL=" $shutdown | tail -n1 | sed -e "s/^INTERVAL=//" -e 's/"//g' -e 's/#.*//')
+# DREAMTIME=$(grep "^DREAMTIME=" $shutdown | tail -n1 | sed -e "s/^DREAMTIME=//" -e 's/"//g' -e 's/#.*//')
+# MIN_WAKEUP_VAR=$(grep "^MIN_WAKEUP_VAR=" $shutdown | tail -n1 | sed -e "s/^MIN_WAKEUP_VAR=//" -e 's/"//g' -e 's/#.*//')
+# WAITING_PERIOD=$(grep "^WAITING_PERIOD=" $shutdown | tail -n1 | sed -e "s/^WAITING_PERIOD=//" -e 's/"//g' -e 's/#.*//')
+
+ # Menu --------------------------------------------------------------------------------------------------------------------------->
+ SendCmd "enterlocal" || return $false
+ SendCmd "menu=New Menu 'Wakeup'" || return $false
+ SendCmd "menu.SetColorKeyText -blue Help" || return $false
+ SendCmd "menu.SetColumns 24" || return $false
+ SendCmd "menu.EnableEvent keyOk close" || return $false
+ # List --------------------------------------------------------------------------------------------------------------------------->
+ SendCmd "menu.AddNew OsdItem -unselectable '--- System ---'" || return $false
+ SendCmd "opt1=menu.AddNew EditListItem -select '$WAKEUP_MODE_T' 'Aufwach Methode' keine acpi nvram extension-board" || return $false
+ SendCmd "opt1.EnableEvent keyBlue" || return $false
+ SendCmd "opt2=menu.AddNew EditListItem -select '$NEED_REBOOT_T' 'Neustart erforderlich' nein ja immer" || return $false
+ SendCmd "opt2.EnableEvent keyBlue" || return $false
+ SendCmd "opt3=menu.AddNew EditListItem -select '$LILO_T' 'Bootmanger' grub lilo" || return $false
+ SendCmd "opt3.EnableEvent keyBlue" || return $false
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Nvram ---'" || return $false
+ SendCmd "opt4=menu.AddNew EditStrItem 'Konfiguration' '$NVRAM_CONFIG'" || return $false
+ SendCmd "opt4.EnableEvent keyBlue" || return $false
+ SendCmd "opt5=menu.AddNew EditStrItem 'IW Name' '$NVRAM_IWNAME'" || return $false
+ SendCmd "opt5.EnableEvent keyBlue" || return $false
+ SendCmd "opt6=menu.AddNew EditStrItem 'Optionen' '$NVRAM_OPT'" || return $false
+ SendCmd "opt6.EnableEvent keyBlue" || return $false
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Nur fuer Experten ---'" || return $false
+ SendCmd "optA=menu.AddNew EditIntItem 'Vorlauf in Minuten' '$WAKEUP_BEFORE'" || return $false
+ SendCmd "optA.EnableEvent keyBlue" || return $false
+ SendCmd "optB=menu.AddNew EditStrItem 'Maximale Auszeit' '$MAX_POWEROFF_TIME'" || return $false
+ SendCmd "optB.EnableEvent keyBlue" || return $false
+ SendCmd "optC=menu.AddNew EditIntItem -max 31 'Interval in Tagen' '$INTERVAL'" || return $false
+ SendCmd "optC.EnableEvent keyBlue" || return $false
+ SendCmd "optD=menu.AddNew EditStrItem 'Wunsch Aufwachzeit' '$DREAMTIME'" || return $false
+ SendCmd "optD.EnableEvent keyBlue" || return $false
+ SendCmd "optE=menu.AddNew EditIntItem -max 3 'Aktion' '$MIN_WAKEUP_VAR'" || return $false
+ SendCmd "optE.EnableEvent keyBlue" || return $false
+ SendCmd "optF=menu.AddNew EditIntItem 'Warte Zeit' '$WAITING_PERIOD'" || return $false
+ SendCmd "optF.EnableEvent keyBlue" || return $false
+ # <---------------------------------------------------------------------------------------------------------------------------- List
+ SendCmd "_focus.addsubmenu menu" || return $false
+ SendCmd "opt1.SetCurrent" || return $false
+ SendCmd "menu.show" || return $false
+ # <---------------------------------------------------------------------------------------------------------------------------- Menu
+
+ while true; do
+ SendCmd "menu.SleepEvent" || return $false
+ # keyOk
+ if IsEvent menu keyOk ; then
+ # WAKEUP_MODE
+ SendCmd "opt1.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$WAKEUP_MODE_T" != "${reply6xx[1]}" ]; then
+ (( WAKEUP_MODE_N = ${reply6xx[1]} - 1 ))
+ (( WAKEUP_MODE = $WAKEUP_MODE_T - 1 ))
+ DoLog "changing WAKEUP_MODE to $WAKEUP_MODE_N"
+ sed -i $shutdown -e "/^WAKEUP_MODE=/s/$WAKEUP_MODE/$WAKEUP_MODE_N/"
+ fi
+ # NEED_REBOOT
+ SendCmd "opt2.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$NEED_REBOOT_T" != "${reply6xx[1]}" ]; then
+ (( NEED_REBOOT_N = ${reply6xx[1]} - 1 ))
+ (( NEED_REBOOT = $NEED_REBOOT_T - 1 ))
+ DoLog "changing NEED_REBOOT to $NEED_REBOOT_N"
+ sed -i $shutdown -e "/^NEED_REBOOT=/s/$NEED_REBOOT/$NEED_REBOOT_N/"
+ fi
+ # LILO
+ SendCmd "opt3.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$LILO_T" != "${reply6xx[1]}" ]; then
+ (( LILO_N = ${reply6xx[1]} - 1 ))
+ (( LILO = $LILO_T - 1 ))
+ DoLog "changing LILO to $LILO_N"
+ sed -i $shutdown -e "/^LILO=/s/$LILO/$LILO_N/"
+ fi
+ # NVRAM_CONFIG
+ SendCmd "opt4.GetValue -quoted" || return $false
+ if [ "${reply5xx[0]}" = 500 ] || [ "${reply2xx[0]}" = 200 ]; then
+ value=$(echo " ${reply5xx[1]}" | sed -e 's/^ //g' -e 's/\\r//g')
+ if [ "$NVRAM_CONFIG" != "$value" ]; then
+ if [ -f "$value" ]; then
+ DoLog "changing NVRAM_CONFIG to $value"
+ sed -i $shutdown -e "/^NVRAM_CONFIG=/s/\"//g" -e "s?^NVRAM_CONFIG=$NVRAM_CONFIG?NVRAM_CONFIG=\"$value\"?"
+ else
+ DoLog "error: file \"$value\" for NVRAM_CONFIG does not exist"
+ fi
+ fi
+ fi
+ # NVRAM_IWNAME
+ SendCmd "opt5.GetValue -quoted" || return $false
+ if [ "${reply5xx[0]}" = 500 ] || [ "${reply2xx[0]}" = 200 ]; then
+ value=$(echo " ${reply5xx[1]}" | sed -e 's/^ //g' -e 's/\\r//g')
+ if [ "$NVRAM_IWNAME" != "$value" ]; then
+ DoLog "changing NVRAM_IWNAME to $value"
+ sed -i $shutdown -e "/^NVRAM_IWNAME=/s/\"//g" -e "s?^NVRAM_IWNAME=$NVRAM_IWNAME?NVRAM_IWNAME=\"$value\"?"
+ fi
+ fi
+ # NVRAM_OPT
+ SendCmd "opt6.GetValue -quoted" || return $false
+ if [ "${reply5xx[0]}" = 500 ] || [ "${reply2xx[0]}" = 200 ]; then
+ value=$(echo " ${reply5xx[1]}" | sed -e 's/^ //g' -e 's/\\r//g')
+ if [ "$NVRAM_OPT" != "$value" ]; then
+ DoLog "changing NVRAM_OPT to $value"
+ sed -i $shutdown -e "/^NVRAM_OPT=/s/\"//g" -e "s?^NVRAM_OPT=$NVRAM_OPT?NVRAM_OPT=\"$value\"?"
+ fi
+ fi
+ # WAKEUP_BEFORE
+ SendCmd "optA.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$WAKEUP_BEFORE" != "${reply6xx[1]}" ]; then
+ DoLog "changing WAKEUP_BEFORE to ${reply6xx[1]}"
+ sed -i $shutdown -e "/^WAKEUP_BEFORE=/s/$WAKEUP_BEFORE/${reply6xx[1]}/"
+ fi
+ # MAX_POWEROFF_TIME
+ SendCmd "optB.GetValue -quoted" || return $false
+ if [ "${reply5xx[0]}" = 500 ] || [ "${reply2xx[0]}" = 200 ]; then
+ value=$(echo " ${reply5xx[1]}" | sed -e 's/^ //g' -e 's/\\r//g')
+ if [ "$MAX_POWEROFF_TIME" != "$value" ]; then
+ DoLog "changing MAX_POWEROFF_TIME to $value"
+ sed -i $shutdown -e "/^MAX_POWEROFF_TIME=/s/\"//g" -e "s?^MAX_POWEROFF_TIME=$MAX_POWEROFF_TIME?MAX_POWEROFF_TIME=\"$value\"?"
+ fi
+ fi
+ # INTERVAL
+ SendCmd "optC.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$INTERVAL" != "${reply6xx[1]}" ]; then
+ DoLog "changing INTERVAL to ${reply6xx[1]}"
+ sed -i $shutdown -e "/^INTERVAL=/s/$INTERVAL/${reply6xx[1]}/"
+ fi
+ # DREAMTIME
+ SendCmd "optD.GetValue -quoted" || return $false
+ if [ "${reply5xx[0]}" = 500 ] || [ "${reply2xx[0]}" = 200 ]; then
+ value=$(echo " ${reply5xx[1]}" | sed -e 's/^ //g' -e 's/\\r//g')
+ if [ "$DREAMTIME" != "$value" ]; then
+ [ -n "$(echo $value | grep '^[0-9]:[0-9][0-9]$')" ] && value="0${value}"
+ if [ -n "$(echo $value | grep '^[0-9][0-9]:[0-9][0-9]$')" ]; then
+ DoLog "changing DREAMTIME to $value"
+ sed -i $shutdown -e "/^DREAMTIME=/s/\"//g" -e "s?^DREAMTIME=$DREAMTIME?DREAMTIME=\"$value\"?"
+ else
+ DoLog "error: value \"$value\" for DREAMTIME has bad format"
+ fi
+ fi
+ fi
+ # MIN_WAKEUP_VAR
+ SendCmd "optE.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$MIN_WAKEUP_VAR" != "${reply6xx[1]}" ]; then
+ DoLog "changing MIN_WAKEUP_VAR to ${reply6xx[1]}"
+ sed -i $shutdown -e "/^MIN_WAKEUP_VAR=/s/$MIN_WAKEUP_VAR/${reply6xx[1]}/"
+ fi
+ # WAITING_PERIOD
+ SendCmd "optF.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "$WAITING_PERIOD" != "${reply6xx[1]}" ]; then
+ DoLog "changing WAITING_PERIOD to ${reply6xx[1]}"
+ sed -i $shutdown -e "/^WAITING_PERIOD=/s/$WAITING_PERIOD/${reply6xx[1]}/"
+ fi
+ # close the submenu
+ if [ "$vdrsetup_ok_ends" = "on" ]; then
+ SendCmd "menu.SendState osBack" || return $false
+# SendCmd "delete menu" || return $false
+# SendCmd "leavelocal" || return $false
+# return $true
+ fi
+ fi
+ # keyBlue (Help)
+ if IsEvent opt1 keyBlue ; then
+ SendCmd "Message -seconds 5 'Optionen: keine, acpi, nvram, extension-board'" || return $false
+ fi
+ if IsEvent opt2 keyBlue ; then
+ SendCmd "Message -seconds 5 'Optionen: nein, ja, immer'" || return $false
+ fi
+ if IsEvent opt3 keyBlue ; then
+ SendCmd "Message -seconds 5 'Optionen: grub, lilo'" || return $false
+ fi
+ if IsEvent opt4 keyBlue ; then
+ SendCmd "Message -seconds 5 'Pfad zur nvram-wakeup Konfiguration'" || return $false
+ fi
+ if IsEvent opt5 keyBlue ; then
+ SendCmd "Message -seconds 5 'Specify the iw (infowriter) name (e.g: gigabyte_5aa)'" || return $false
+ fi
+ if IsEvent opt6 keyBlue ; then
+ SendCmd "Message -seconds 5 'Versuche \"nvram-wakeup --help\" auf einem Terminal'" || return $false
+ fi
+ if IsEvent optA keyBlue ; then
+ SendCmd "Message -seconds 5 'Optionen: 0, MAXIMAL, INTERVAL oder Zeit in Minuten'" || return $false
+ fi
+ if IsEvent optB keyBlue ; then
+ SendCmd "Message -seconds 5 'Gebe den Interval in Tagen an'" || return $false
+ fi
+ if IsEvent optC keyBlue ; then
+ SendCmd "Message -seconds 5 'Aufwachzeit in Stunden:Minuten zB 03:00'" || return $false
+ fi
+ if IsEvent optD keyBlue ; then
+ SendCmd "Message -seconds 5 'Aktion wenn \$MIN_WAKEUPTIME -gt \$WAKEUPTIME'" || return $false
+ fi
+ if IsEvent optE keyBlue ; then
+ SendCmd "Message -seconds 5 'Wartezeit bis zum erneuten Shutdown in Minuten'" || return $false
+ fi
+ # close the submenu
+ if IsEvent menu close ; then
+ SendCmd "delete menu" || return $false
+ SendCmd "leavelocal" || return $false
+ return $true
+ fi
+ done
+}
+
+##### DVB-Modul Menu #####
+function DVBModul() {
+ # dvb_ttpci parameter description
+ debug_desc="debug level (bitmask, default 0)"
+ vidmode_desc="analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC"
+ pids_off_desc="clear video/audio/PCR PID filters when demux is closed"
+ adac_desc="audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)"
+ hw_sections_desc="0 use software section filter, 1 use hardware"
+ rgb_on_desc="For Siemens DVB-C cards only: Enable RGB control signal on SCART pin 16 to switch SCART video mode from CVBS to RGB"
+ volume_desc="initial volume: default 255 (range 0-255)"
+ budgetpatch_desc="use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)"
+ wss_cfg_4_3_desc="WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data"
+ wss_cfg_16_9_desc="WSS 16:9 - default 0x0007 - bit 15: disable, 14: burst mode, 13..0: wss data"
+ tv_standard_desc="TV standard: 0 PAL (default), 1 NTSC"
+
+
+# { 0x7, "16:9" },
+# { 0x8, "4:3" },
+# { 0x1, "L14:9" },
+# { 0xb, "L16:9" },
+# { 0xd, "L>16:9" }
+
+
+ TT_PARAMETERS="options dvb_ttpci"
+ if [ -d /sys/module/dvb_ttpci/parameters ]; then
+ for p in $(ls /sys/module/dvb_ttpci/parameters); do
+ eval ${p}=`cat /sys/module/dvb_ttpci/parameters/${p}`
+ done
+ fi
+
+ # defaults and some tests
+ vidmode=$(echo $vidmode | grep '^[0,1,2,3]$')
+ [ -n "$vidmode" ] || vidmode=1
+ [ "$vidmode" = "0" ] && vidmode=4
+ tv_standard=$(echo $tv_standard | grep '^[0,1]$')
+ [ -n "$tv_standard" ] || tv_standard=0
+ (( tv_standard = $tv_standard + 1 ))
+ wss_cfg_16_9=$(echo $wss_cfg_16_9 | grep '^[[:digit:]]*$')
+ [ -n "$wss_cfg_16_9" ] || wss_cfg_16_9=7
+ wss_cfg_4_3=$(echo $wss_cfg_4_3 | grep '^[[:digit:]]*$')
+ [ -n "$wss_cfg_4_3" ] || wss_cfg_4_3=16392
+ debug=$(echo $debug | grep '^[[:digit:]]*$')
+ [ -n "$debug" ] || debug=0
+ ir_inversion=$(echo $ir_inversion | cut -f1 -d ',' | grep '^[0,1]$')
+ [ -n "$ir_inversion" ] || ir_inversion=0
+ (( ir_inversion = $ir_inversion + 1 ))
+ volume=$(echo $volume | grep '^[[:digit:]]*$')
+ [ -n "$volume" ] || volume=255
+
+
+ SendCmd "enterlocal" || return $false # Preserve global variable space, so we can re-use 'menu'
+ SendCmd "menu=New Menu 'DVB Module'" || return $false
+ SendCmd "menu.SetColorKeyText -blue Help" || return $false
+ SendCmd "menu.SetColumns 24" || return $false
+ SendCmd "menu.EnableEvent keyOk close" || return $false
+ # List ->
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Video ---'" || return $false
+ SendCmd "opt1=menu.AddNew EditListItem -select '$vidmode' 'Video Ausgang' RGB/Composite S-Video/Composite S-Video off" || return $false
+ SendCmd "opt1.EnableEvent keyBlue" || return $false
+ SendCmd "opt2=menu.AddNew EditListItem -select '$tv_standard' 'TV Standard' PAL NTSC" || return $false
+ SendCmd "opt2.EnableEvent keyBlue" || return $false
+ SendCmd "opt3=menu.AddNew EditIntItem 'WSS 16:9' '$wss_cfg_16_9'" || return $false
+# SendCmd "opt3=menu.AddNew EditListItem -select '$wss_cfg_16_9' 'WSS 16:9' 16:9 1 2 " || return $false
+ SendCmd "opt3.EnableEvent keyBlue" || return $false
+ SendCmd "opt4=menu.AddNew EditIntItem 'WSS 4:3' '$wss_cfg_4_3'" || return $false
+ SendCmd "opt4.EnableEvent keyBlue" || return $false
+ SendCmd "menu.AddNew OsdItem -unselectable ''" || return $false
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Sonstiges ---'" || return $false
+ SendCmd "opt5=menu.AddNew EditIntItem 'Debug Level' '$debug'" || return $false
+ SendCmd "opt5.EnableEvent keyBlue" || return $false
+ SendCmd "opt6=menu.AddNew EditListItem -select '$ir_inversion' 'IR invertieren' off on" || return $false
+ SendCmd "opt6.EnableEvent keyBlue" || return $false
+ SendCmd "opt7=menu.AddNew EditIntItem -max 255 'Lautstaerke' '$volume'" || return $false
+ SendCmd "opt7.EnableEvent keyBlue" || return $false
+ # <- List
+ SendCmd "_focus.addsubmenu menu" || return $false
+ SendCmd "opt1.SetCurrent" || return $false
+ SendCmd "menu.show" || return $false
+
+ while true; do
+ SendCmd "menu.SleepEvent" || return $false
+ # keyOk
+ if IsEvent menu keyOk ; then
+ # vidmode
+ SendCmd "opt1.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "1" != "${reply6xx[1]}" ]; then
+ vidmode="${reply6xx[1]}"
+ [ "$vidmode" = "4" ] && vidmode=0
+ DoLog "adding vidmode=${reply6xx[1]} to TT_PARAMETERS"
+ TT_PARAMETERS="$TT_PARAMETERS vidmode=${reply6xx[1]}"
+ fi
+ # tv_standard
+ SendCmd "opt2.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "1" != "${reply6xx[1]}" ]; then
+ (( tv_standard = ${reply6xx[1]} - 1 ))
+ DoLog "adding tv_standard=$tv_standard to TT_PARAMETERS"
+ TT_PARAMETERS="$TT_PARAMETERS tv_standard=$tv_standard"
+ fi
+ # wss_cfg_16_9
+ SendCmd "opt3.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "7" != "${reply6xx[1]}" ]; then
+ DoLog "adding wss_cfg_16_9=${reply6xx[1]} to TT_PARAMETERS"
+ TT_PARAMETERS="$TT_PARAMETERS wss_cfg_16_9=${reply6xx[1]}"
+ fi
+ # wss_cfg_4_3
+ SendCmd "opt4.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "16392" != "${reply6xx[1]}" ]; then
+ DoLog "adding wss_cfg_4_3=${reply6xx[1]} to TT_PARAMETERS"
+ TT_PARAMETERS="$TT_PARAMETERS wss_cfg_4_3=${reply6xx[1]}"
+ fi
+ # debug
+ SendCmd "opt5.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "0" != "${reply6xx[1]}" ]; then
+ DoLog "adding debug=${reply6xx[1]} to TT_PARAMETERS"
+ TT_PARAMETERS="$TT_PARAMETERS debug=${reply6xx[1]}"
+ fi
+ # ir_inversion
+ SendCmd "opt6.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "1" != "${reply6xx[1]}" ]; then
+ (( ir_inversion = ${reply6xx[1]} - 1 ))
+ DoLog "adding tv_standard=$ir_inversion to TT_PARAMETERS"
+ TT_PARAMETERS="$TT_PARAMETERS tv_standard=$ir_inversion"
+ fi
+ # volume
+ SendCmd "opt7.GetValue" || return $false
+ [ "${reply6xx[0]}" != 600 ] && return $false
+ if [ "255" != "${reply6xx[1]}" ]; then
+ DoLog "adding volume=${reply6xx[1]} to TT_PARAMETERS"
+ TT_PARAMETERS="$TT_PARAMETERS volume=${reply6xx[1]}"
+ fi
+
+ # write config to /etc/modprobe.d/dvb
+ echo $TT_PARAMETERS > /etc/modprobe.d/dvb
+
+ # close the submenu
+ if [ "$vdrsetup_ok_ends" = "on" ]; then
+ SendCmd "menu.SendState osBack" || return $false
+# SendCmd "delete menu" || return $false
+# SendCmd "leavelocal" || return $false
+# return $true
+ fi
+ fi
+ # keyBlue (Help)
+ if IsEvent opt1 keyBlue ; then
+ SendCmd "Message -seconds 5 'Video Modus des J2 - default RGB'" || return $false
+ fi
+ if IsEvent opt2 keyBlue ; then
+ SendCmd "Message -seconds 5 'TV Standard - default Pal'" || return $false
+ fi
+ if IsEvent opt3 keyBlue ; then
+ SendCmd "Message -seconds 5 'WSS 16/9 - default 7'" || return $false
+ fi
+ if IsEvent opt4 keyBlue ; then
+ SendCmd "Message -seconds 5 'WSS 4/3 - default 16392'" || return $false
+ fi
+ if IsEvent opt5 keyBlue ; then
+ SendCmd "Message -seconds 5 'Debug Level - default 0'" || return $false
+ fi
+ if IsEvent opt6 keyBlue ; then
+ SendCmd "Message -seconds 5 'IR-Signal invertieren - default off'" || return $false
+ fi
+ if IsEvent opt7 keyBlue ; then
+ SendCmd "Message -seconds 5 'Start Lautstaerke von 0-255 - default 255'" || return $false
+ fi
+ # close the submenu
+ if IsEvent menu close ; then
+ SendCmd "delete menu" || return $false
+ SendCmd "leavelocal" || return $false
+ return $true
+ fi
+ done
+}
+
+
+##### x-vdr #############
+function InstallPlugins {
+ [ "$have_xvdr" = "on" ] || return $false
+ xdir=/usr/local/src/x-vdr
+ cmd=$1
+ # Menu -------------------------------------------------------------------------------------------------------->
+ SendCmd "enterlocal" || return $false
+ if [ "$cmd" = "installed" ]; then
+ SendCmd "menu=New Menu 'Installierte Plugins'" || return $false
+ SendCmd "menu.SetColorKeyText -red 'Edit' -green 'Reinstall' -yellow 'Remove' -blue 'Help'" || return $false
+ SendCmd "menu.SetColumns 4" || return $false
+ elif [ "$cmd" = "standby" ]; then
+ SendCmd "menu=New Menu 'Installierbare Plugins'" || return $false
+ SendCmd "menu.SetColorKeyText -red 'Edit' -green 'Install' -blue 'Help'" || return $false
+ SendCmd "menu.SetColumns 4 16" || return $false
+ else
+ SendCmd "menu=New Menu 'Alle Plugins'" || return $false
+ SendCmd "menu.SetColorKeyText -red 'Edit' -green 'Install' -yellow 'Remove' -blue 'Help'" || return $false
+ SendCmd "menu.SetColumns 4 16" || return $false
+ fi
+
+ SendCmd "menu.EnableEvent keyOk keyRed keyGreen keyYellow keyBlue close" || return $false
+ SendCmd "menu.EnableEvent key1 key2 key3 key4 key5 key6 key7 key8 key9 key0" || return $false
+ # List--------------------------------------------------------------------------------------------------------->
+ SendCmd "Message -status 'Menu wird aufgebaut ...'" || return $false
+ plugs=$(ls -B "$xdir/plugins")
+ if [ -n "$plugs" ]; then
+ n=0
+ for plug in $plugs ; do
+# eval local $(grep -r -m1 "^${plug}=" $xdir/setup.conf) # status
+# if [ "x${!plug}" = "xon" ] || [ "x${!plug}" = "xoff" ]; then
+# fi
+ ins_status="nicht installiert"
+ if [ "$plug" = "streamdev" ]; then
+ [ -f "${VDRLIBDIR}/libvdr-streamdev-server.so.${plugin_api}" ] && ins_status="installiert"
+ [ -f "${VDRLIBDIR}/libvdr-streamdev-client.so.${plugin_api}" ] && ins_status="installiert"
+ plugin_desc="streamdev_server_desc"
+ desc="${!plugin_desc}"
+ [ -n "$desc" ] || {
+ plugin_desc="streamdev_client_desc"
+ desc="${!plugin_desc}"
+ }
+ [ -n "$desc" ] || desc="$plug"
+ else
+ [ -f "${VDRLIBDIR}/libvdr-${plug}.so.${plugin_api}" ] && ins_status="installiert"
+ plugin_desc="${plug}_desc"
+ desc="${!plugin_desc}"
+ [ -n "$desc" ] || desc="$plug"
+ fi
+ if [ "$cmd" = "installed" ]; then
+ if [ "$ins_status" = "installiert" ]; then
+ ((n=n+1))
+ [ -n "$(echo $n | grep "^[0-9][0-9][0-9]$")" ] && N="$n"
+ [ -n "$(echo $n | grep "^[0-9][0-9]$")" ] && N=" $n"
+ [ -n "$(echo $n | grep "^[0-9]$")" ] && N=" $n"
+ SendCmd "$plug=menu.AddNew OsdItem '$N \t$desc'" || return $false
+ fi
+ elif [ "$cmd" = "standby" ]; then
+ if [ "$ins_status" != "installiert" ]; then
+ ((n=n+1))
+ [ -n "$(echo $n | grep "^[0-9][0-9][0-9]$")" ] && N="$n"
+ [ -n "$(echo $n | grep "^[0-9][0-9]$")" ] && N=" $n"
+ [ -n "$(echo $n | grep "^[0-9]$")" ] && N=" $n"
+ SendCmd "$plug=menu.AddNew OsdItem '$N \t$plug \t$ins_status'" || return $false
+ fi
+ else # all
+ ((n=n+1))
+ [ -n "$(echo $n | grep "^[0-9][0-9][0-9]$")" ] && N="$n"
+ [ -n "$(echo $n | grep "^[0-9][0-9]$")" ] && N=" $n"
+ [ -n "$(echo $n | grep "^[0-9]$")" ] && N=" $n"
+ SendCmd "$plug=menu.AddNew OsdItem '$N \t$plug \t$ins_status'" || return $false
+ fi
+ done
+ fi
+ SendCmd "Message -statusclear" || return $false
+ # <-------------------------------------------------------------------------------------------------------- List
+ SendCmd "_focus.addsubmenu menu" || return $false
+ SendCmd "menu.Show" || return $false
+ # <-------------------------------------------------------------------------------------------------------- Menu
+
+ while true ; do
+ SendCmd "menu.SleepEvent" || return $false
+ # keyOk and keyRed
+ # if IsEvent menu keyOk || IsEvent menu keyRed; then
+ # keyRed (Edit)
+ if IsEvent menu keyRed; then
+ SendCmd "menu.GetCurrent"
+ [ "${reply3xx[0]}" != 302 ] && return $false
+ PluginSetup "${reply3xx[2]}" || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyGreen (Install)
+ if IsEvent menu keyGreen ; then
+ SendCmd "menu.GetCurrent"
+ [ "${reply3xx[0]}" != 302 ] && return $false
+ n="${reply3xx[1]}"; N=;
+ [ -n "$(echo $n | grep "^[0-9][0-9][0-9]$")" ] && N="$n"
+ [ -n "$(echo $n | grep "^[0-9][0-9]$")" ] && N=" $n"
+ [ -n "$(echo $n | grep "^[0-9]$")" ] && N=" $n"
+ plugin="${reply3xx[2]}"
+ if [ "$cmd" = "installed" ]; then
+ SendCmd "Message -warn -seconds 10 '$plugin neu installieren?'" || return $false
+ else
+ SendCmd "Message -warn -seconds 10 '$plugin installieren?'" || return $false
+ fi
+ if IsEvent Message keyOk ; then
+ SendCmd "Message -status 'Installiere $plugin ...'" || return $false
+ cd $xdir/plugins/$plugin
+ plugin_status=$(./plugin.sh -s)
+ case $plugin_status in
+ 0 ) ./plugin.sh -m && {
+ $VDRPRG --version | sed -e 's/\(^[^ ]*\)\(.*$\)/\1_desc="\1\2"/' -e '/^streamdev/s/-/_/' -e 's/^\[.*//' | sort > /tmp/vdr-desc.cache
+ . /tmp/vdr-desc.cache
+ } ;;
+ * ) ./plugin.sh -r ;;
+ esac
+ SendCmd "Message -statusclear" || return $false
+ SendCmd "$plugin.SETTEXT '$N \t$plugin \tinstalliert'"
+ SendCmd "menu.Show" || return $false
+ fi
+ fi
+ # keyYellow (Remove)
+ if [ "$cmd" != "standby" ]; then
+ if IsEvent menu keyYellow ; then
+ SendCmd "menu.GetCurrent"
+ [ "${reply3xx[0]}" != 302 ] && return $false
+ n="${reply3xx[1]}"; N=;
+ [ -n "$(echo $n | grep "^[0-9][0-9][0-9]$")" ] && N="$n"
+ [ -n "$(echo $n | grep "^[0-9][0-9]$")" ] && N=" $n"
+ [ -n "$(echo $n | grep "^[0-9]$")" ] && N=" $n"
+ plugin="${reply3xx[2]}"
+ cd $xdir/plugins/$plugin
+ plugin_status=$(./plugin.sh -s)
+ case $plugin_status in
+ 0 ) SendCmd "Message -seconds 5 '$plugin ist nicht installiert'" || return $false ;;
+ * ) SendCmd "Message -warn -seconds 10 'Soll $plugin entfernt werden?'" || return $false
+ if IsEvent Message keyOk ; then
+ SendCmd "Message -status 'Entferne $plugin ...'" || return $false
+ ./plugin.sh -c
+ SendCmd "Message -statusclear" || return $false
+ SendCmd "$plugin.SETTEXT '$N \t$plugin'"
+ SendCmd "menu.Show" || return $false
+ fi ;;
+ esac
+ fi
+ fi
+ # keyBlue (Help)
+ if IsEvent menu keyBlue ; then
+ SendCmd "Message -seconds 5 'Auswahl mit Farbtasten oder Menue verlassen mit Exit'" || return $false
+ fi
+ # close the submenu
+ if IsEvent menu close ; then
+ SendCmd "delete menu" || return $false
+ SendCmd "leavelocal" || return $false
+ return $true
+ fi
+ # keyNr
+ if [ $n -gt 0 ]; then
+ mykey=;
+ k1=;
+ for k1 in 0 1 2 3 4 5 6 7 8 9 ; do
+ if IsEvent menu key$k1 ; then
+ mykey=$k1
+ [ $n -gt 9 ] || break
+ SendCmd "menu.SleepEvent -timeoutms $vdrsetup_key_timeout" || return $false
+ if IsEvent menu keyOk ; then break; fi
+ k2=;
+ for k2 in 0 1 2 3 4 5 6 7 8 9 ; do
+ if IsEvent menu key$k2 ; then
+ mykey=${k1}${k2}
+ [ $n -gt 99 ] || break
+ SendCmd "menu.SleepEvent -timeoutms $vdrsetup_key_timeout" || return $false
+ if IsEvent menu keyOk ; then break; fi
+ k3=;
+ for k3 in 0 1 2 3 4 5 6 7 8 9 ; do
+ if IsEvent menu key$k3 ; then
+ mykey=${k1}${k2}${k3}
+ break
+ fi
+ done
+ break
+ fi
+ done
+ break
+ fi
+ done
+
+ if [ -n "$mykey" ]; then
+ [ $mykey -gt $n ] && mykey=$n
+ ((mykey=mykey-1)) # we do this, because 0 is the first item
+ [ $mykey -lt 0 ] && mykey=0
+ SendCmd "menu.SetCurrent $mykey" || return $false
+# SendCmd "menu.GetCurrent"
+# [ "${reply3xx[0]}" != 302 ] && return $false
+# PluginSetup "${reply3xx[2]}" || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ fi
+ done
+}
+
+function BackupVdrConf() {
+ [ "$have_xvdr" = "on" ] || return $false
+ xdir=/usr/local/src/x-vdr
+ SendCmd "Message -status 'Backup wird erstellt ...'" || return $false
+ BACKUPDIR="/tmp/vdr-backup"
+ BACKUPPATH="$xdir/backup"
+ [ ! -d $BACKUPPATH ] && mkdir -p $BACKUPPATH
+ [ -d $BACKUPDIR ] && rm -rf $BACKUPDIR
+ mkdir -p $BACKUPDIR
+ cp -pfR --parents $VDRCONFDIR $BACKUPDIR
+ cp -pfR --parents $VDRSCRIPTDIR $BACKUPDIR
+ cp -pf --parents $VDRBINDIR/runvdr $BACKUPDIR
+ cp -pf --parents /etc/default/vdr $BACKUPDIR
+ cp -pf --parents /etc/init.d/vdr $BACKUPDIR
+ BACKUPFILE=$BACKUPPATH/`date +%y%m%d-%H%M`-vdr-conf.tar.gz
+ cd $BACKUPDIR
+ tar -zcf $BACKUPFILE * && SendCmd "Message -seconds 5 'Backup erstellt'" || return $false
+ cd ..
+ rm -rf $BACKUPDIR
+ SendCmd "Message -statusclear" || return $false
+}
+
+function Xvdr() {
+ [ "$have_xvdr" = "on" ] || return $false
+ xdir=/usr/local/src/x-vdr
+ # Menu --------------------------------------------------------------------------------------------------------------->
+ SendCmd "enterlocal" || return $false
+ SendCmd "menu=New Menu 'x-vdr'" || return $false
+ SendCmd "menu.SetColorKeyText -red 'Reboot' -green 'Shutdown' -yellow 'VDR Restart' -blue 'Help'" || return $false
+ SendCmd "menu.SetColumns 1 2" || return $false
+ SendCmd "menu.EnableEvent keyRed keyGreen keyYellow close" || return $false
+ SendCmd "menu.EnableEvent key1 key2 key3 key4 key5 key6 key7 key8 key9 key0" || return $false
+ # List --------------------------------------------------------------------------------------------------------------->
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Plugins ---'" || return $false
+ SendCmd "optP1=menu.AddNew OsdItem '\t1\tAlle Plugins'" || return $false
+ SendCmd "optP1.EnableEvent keyOk keyBlue" || return $false
+ SendCmd "optP2=menu.AddNew OsdItem '\t2\tInstallierte Plugins'" || return $false
+ SendCmd "optP2.EnableEvent keyOk keyBlue" || return $false
+ SendCmd "optP3=menu.AddNew OsdItem '\t3\tInstallierbare Plugins'" || return $false
+ SendCmd "optP3.EnableEvent keyOk keyBlue"
+
+ SendCmd "menu.AddNew OsdItem -unselectable '--- Backup ---'" || return $false
+ SendCmd "optB1=menu.AddNew OsdItem '\t3\tKonfiguration und Skripte'" || return $false
+ SendCmd "optB1.EnableEvent keyOk keyBlue" || return $false
+ # <--------------------------------------------------------------------------------------------------------------- List
+ SendCmd "_focus.addsubmenu menu" || return $false
+ SendCmd "opt1.SetCurrent" || return $false
+ SendCmd "menu.show" || return $false
+ # <--------------------------------------------------------------------------------------------------------------- Menu
+
+ while true; do
+ SendCmd "menu.SleepEvent" || return $false
+ # keyOk (SubMenu InstallPlugins)
+ if IsEvent optP1 keyOk || IsEvent menu key1 ; then
+ InstallPlugins all || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ if IsEvent optP2 keyOk || IsEvent menu key2 ; then
+ InstallPlugins installed || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ if IsEvent optP3 keyOk || IsEvent menu key2 ; then
+ InstallPlugins standby || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyOk (Backup)
+ if IsEvent optB1 keyOk || IsEvent menu key3 ; then
+ BackupVdrConf || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyRed (Reboot)
+ if IsEvent menu keyRed ; then
+ SendCmd "Message -warn -seconds 10 'System jetzt neustarten?'" || return $false
+ if IsEvent Message keyOk ; then
+ SendCmd "Message 'System wird neugestartet...'" || return $false
+ SendCmd "menu.SendState osEnd" || return $false
+ shutdown -r now
+ fi
+ fi
+ # keyGreen (Shutdown)
+ if IsEvent menu keyGreen ; then
+ SendCmd "Message -warn -seconds 10 'System jetzt ausschalten?'" || return $false
+ if IsEvent Message keyOk ; then
+# SendCmd "Message 'System wird ausgeschaltet...'" || return $false
+ SendCmd "menu.SendState osEnd" || return $false
+# shutdown -h now
+ /usr/bin/svdrpsend.pl HITK Power
+ return $true
+ fi
+ fi
+ # keyYellow (Restart)
+ if IsEvent menu keyYellow ; then
+ SendCmd "Message -warn -seconds 10 'VDR jetzt neustarten?'" || return $false
+ if IsEvent Message keyOk ; then
+ SendCmd "Message 'VDR wird neugestartet...'" || return $false
+ SendCmd "menu.SendState osEnd" || return $false
+ sleep 1
+ killall -9 vdr
+ fi
+ fi
+ # keyBlue (Help)
+ if IsEvent optP1 keyBlue ; then
+ SendCmd "Message -seconds 5 'Zeigt alle verfuegbaren Plugins'" || return $false
+ fi
+ if IsEvent optP2 keyBlue ; then
+ SendCmd "Message -seconds 5 'Zeigt nur die installierten Plugins'" || return $false
+ fi
+ if IsEvent optP3 keyBlue ; then
+ SendCmd "Message -seconds 5 'Zeigt nur die nicht installierten Plugins'" || return $false
+ fi
+ if IsEvent optB1 keyBlue ; then
+ SendCmd "Message -seconds 5 'Erstellt ein Backup von Konfiguration und Skripten'" || return $false
+ fi
+
+ # close the submenu
+ if IsEvent menu close ; then
+ SendCmd "delete menu" || return $false
+ SendCmd "leavelocal" || return $false
+ return $true
+ fi
+ done
+}
+
+
+##### Main Menu #####
+function MainMenu() {
+ # Menu --------------------------------------------------------------------------------------------------------------->
+ SendCmd "menu=New Menu 'vdrsetup'" || return $false
+ SendCmd "menu.SetColorKeyText -red 'Reboot' -green 'Shutdown' -yellow 'VDR Restart' -blue 'Help'" || return $false
+ SendCmd "menu.SetColumns 1 2" || return $false
+ SendCmd "menu.EnableEvent keyRed keyGreen keyYellow keyBlue close" || return $false
+ SendCmd "menu.EnableEvent key1 key2 key3 key4 key5 key6 key7 key8 key9 key0" || return $false
+ # List --------------------------------------------------------------------------------------------------------------->
+ SendCmd "opt1=menu.AddNew OsdItem '\t1\tVDR Einstellungen'" || return $false
+ SendCmd "opt1.EnableEvent keyOk" || return $false
+ SendCmd "opt2=menu.AddNew OsdItem '\t2\tInstallierte Plugins'" || return $false
+ SendCmd "opt2.EnableEvent keyOk" || return $false
+ SendCmd "opt3=menu.AddNew OsdItem '\t3\tPlugin Status Liste'" || return $false
+ SendCmd "opt3.EnableEvent keyOk" || return $false
+ SendCmd "opt4=menu.AddNew OsdItem '\t4\tSystemstart'" || return $false
+ SendCmd "opt4.EnableEvent keyOk" || return $false
+ SendCmd "opt5=menu.AddNew OsdItem '\t5\tWakeup'" || return $false
+ SendCmd "opt5.EnableEvent keyOk" || return $false
+ SendCmd "opt6=menu.AddNew OsdItem '\t6\tSkript Intern'" || return $false
+ SendCmd "opt6.EnableEvent keyOk" || return $false
+ SendCmd "opt7=menu.AddNew OsdItem '\t7\tDVB Modul'" || return $false
+ SendCmd "opt7.EnableEvent keyOk" || return $false
+ if [ "$have_xvdr" = "on" ]; then
+ SendCmd "opt8=menu.AddNew OsdItem '\t8\tX-VDR'" || return $false
+ SendCmd "opt8.EnableEvent keyOk" || return $false
+ fi
+ # <--------------------------------------------------------------------------------------------------------------- List
+ SendCmd "opt1.SetCurrent" || return $false
+ SendCmd "menu.Show" || return $false
+ # <--------------------------------------------------------------------------------------------------------------- Menu
+
+ while true ; do
+ SendCmd "menu.SleepEvent" || return $false
+ # keyOk (SubMenu VdrSetup)
+ if IsEvent opt1 keyOk || IsEvent menu key1 ; then
+ VdrSetup || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyOk (SubMenu ListPlugins)
+ if IsEvent opt2 keyOk || IsEvent menu key2 ; then
+ ListPlugins || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyOk (SubMenu PluginStatusList)
+ if IsEvent opt3 keyOk || IsEvent menu key3 ; then
+ PluginStatusList || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyOk (SubMenu SystemStart)
+ if IsEvent opt4 keyOk || IsEvent menu key4 ; then
+ SystemStart || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyOk (SubMenu Wakeup)
+ if IsEvent opt5 keyOk || IsEvent menu key5 ; then
+ Wakeup || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyOk (SubMenu ScriptSetup)
+ if IsEvent opt6 keyOk || IsEvent menu key6 ; then
+# SendCmd "Message -seconds 5 'Einstellungen gelten nur fuer diese Sitzung'" || return $false
+ ScriptSetup || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyOk (SubMenu DVB)
+ if IsEvent opt7 keyOk || IsEvent menu key7 ; then
+ DVBModul || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ # keyOk (SubMenu Xvdr)
+ if [ "$have_xvdr" = "on" ]; then
+ if IsEvent opt8 keyOk || IsEvent menu key8 ; then
+ Xvdr || return $false
+ SendCmd "menu.Show" || return $false
+ fi
+ fi
+ # keyRed (Reboot)
+ if IsEvent menu keyRed ; then
+ SendCmd "Message -warn -seconds 10 'System jetzt neustarten?'" || return $false
+ if IsEvent Message keyOk ; then
+ SendCmd "Message 'System wird neugestartet...'" || return $false
+ SendCmd "menu.SendState osEnd" || return $false
+ shutdown -r now
+ fi
+ fi
+ # keyGreen (Shutdown)
+ if IsEvent menu keyGreen ; then
+ SendCmd "Message -warn -seconds 10 'System jetzt ausschalten?'" || return $false
+ if IsEvent Message keyOk ; then
+# SendCmd "Message 'System wird ausgeschaltet...'" || return $false
+ SendCmd "menu.SendState osEnd" || return $false
+# shutdown -h now
+ /usr/bin/svdrpsend.pl HITK Power
+ return $true
+ fi
+ fi
+ # keyYellow (Restart)
+ if IsEvent menu keyYellow ; then
+ SendCmd "Message -warn -seconds 10 'VDR jetzt neustarten?'" || return $false
+ if IsEvent Message keyOk ; then
+ SendCmd "Message 'VDR wird neugestartet...'" || return $false
+ SendCmd "menu.SendState osEnd" || return $false
+ sleep 1
+ killall -9 vdr
+ fi
+ fi
+ # keyBlue (Help)
+ if IsEvent menu keyBlue ; then
+ SendCmd "Message -seconds 5 'Auswahl mit OK oder Menue verlassen mit Exit'" || return $false
+ fi
+ # close the menu
+ if IsEvent menu close ; then
+ SendCmd "menu.SendState osEnd" || return $false
+ return $true
+ fi
+ done
+}
+
+
+###############
+# Main Code #
+###############
+
+
+ConnectServer localhost 2010
+# Connect to the server process
+
+ReadReply || error
+# Read server welcome
+
+SendCmd "Version 0.1" || error
+# Identify to server with protocol version
+
+MainMenu || error
+# Do vdrsetup
+
+SendCmd Quit
+DoLog "disconnecting from OSDServer"
+# ... and good bye
+
+exec 3>&-
+exec 4>&-
+# close FIFOs
+
diff --git a/vdr/scripts/vdrshutdown b/vdr/scripts/vdrshutdown
new file mode 100644
index 0000000..bda6766
--- /dev/null
+++ b/vdr/scripts/vdrshutdown
@@ -0,0 +1,214 @@
+#!/bin/bash
+
+# x-vdr (Installations-Skript fuer einen VDR mit Debian als Basis)
+# von Marc Wernecke - www.zulu-entertainment.de
+# 26.01.2009
+
+# vdrshutdown: shutdown script for x-vdr
+
+# no arguments, no action
+[ $# -gt 0 ] || exit 1
+
+# defaults
+DEBUG=0
+LOGGER="logger -s ${0##*/} "
+WAKEUPTIME=0
+
+# load VDR configuration
+if [ -f /etc/default/vdr ]; then
+ . /etc/default/vdr
+else
+ $LOGGER "exit, because /etc/default/vdr does not exists"
+ exit 1
+fi
+
+# load Shutdown configuration
+if [ -f "$VDRCONFDIR/shutdown.conf" ]; then
+ . "$VDRCONFDIR/shutdown.conf"
+else
+ $LOGGER "exit, because $VDRCONFDIR/shutdown.conf does not exists"
+ exit 1
+fi
+
+# check acpi
+if [ "$WAKEUP_MODE" = "1" ]; then
+ if [ -f /sys/class/rtc/rtc0/wakealarm ]; then
+ ACPI="/sys/class/rtc/rtc0/wakealarm"
+ elif [ -f /proc/acpi/alarm ]; then
+ ACPI="/proc/acpi/alarm"
+ else
+ $LOGGER "acpi-wakeup is not supported by your kernel, exit"
+ echo "$VDRBINDIR/svdrpsend.pl MESG 'acpi-wakeup is not supported by your kernel, exit'"| at now
+ exit 1
+ fi
+fi
+
+# check procs
+while read i; do
+ CMD=$(eval $i 2>/dev/null)
+ if [ $? -eq 0 ] && [ -n "$CMD" ]; then
+ $LOGGER "stop shutdown, because $CMD..."
+ $LOGGER "waiting period $WAITING_PERIOD minutes up to the next shutdown..."
+ echo "$VDRBINDIR/svdrpsend.pl MESG 'Shutdown abgebrochen, weil $CMD'"| at now
+ sleep 1
+ echo "$VDRBINDIR/svdrpsend.pl MESG 'Erneuter Shutdown Versuch in $WAITING_PERIOD Minuten.'"| at now
+ sleep 1
+ echo "$VDRBINDIR/svdrpsend.pl HITK Power"| at now +$WAITING_PERIOD minutes
+ exit 1
+ fi
+done < <(echo "$CHECK_PROCS" | sed '/^[ ]*$/d')
+
+# no wakeup-mode
+if [ "$WAKEUP_MODE" = "0" ]; then
+ echo "$VDRBINDIR/svdrpsend.pl MESG 'Shutdown now!'"| at now
+ sudo $VDRSCRIPTDIR/vdr2root shutdown
+ exit 0
+fi
+
+# extension-board
+if [ "$WAKEUP_MODE" = "3" ]; then
+ echo "$VDRBINDIR/svdrpsend.pl MESG 'Shutdown now!'"| at now
+ sudo $VDRSCRIPTDIR/vdr2root extb-poweroff
+ exit 0
+fi
+
+# check MAX_POWEROFF_TIME for special commands and set the value
+if [ "$MAX_POWEROFF_TIME" = "MAXIMAL" ]; then # calculate the maximal wakeup-time
+ MONTH_NOW=`date +%-m` # if this month has no 31th, set the wakeup to the 31th of the next month
+ if [ $MONTH_NOW==2 ] || [ $MONTH_NOW==4 ] || [ $MONTH_NOW==6 ] || [ $MONTH_NOW==9 ] || [ $MONTH_NOW==11 ]; then
+ YEAR_NOW=`date +%Y`
+ NEXT_WAKEUP=`date -d "$YEAR_NOW-$(($MONTH_NOW+1))-31 23:59:59" +%s`
+ MAX_POWEROFF_TIME=$((($NEXT_WAKEUP-`date +%s`) / 60))
+ else # sleep exactly one month
+ MAX_POWEROFF_TIME=44640
+ fi
+ WAKEUPTIME=$((`date +%s` + $MAX_POWEROFF_TIME * 60))
+elif [ "$MAX_POWEROFF_TIME" = "INTERVAL" ]; then # calculate the next wakeup-time for housekeeping
+ DAY_IN_YEAR=`date +%j`
+ DREAMDAY=0
+ while [ $DREAMDAY -le $DAY_IN_YEAR ]; do
+ DREAMDAY=$(($DREAMDAY + $INTERVAL))
+ done
+ YEAR_NOW=`date +%Y`
+ if [ $DREAMDAY -gt 365 ]; then
+ DREAMDAY=$(($DREAMDAY - 365))
+ YEAR_NOW=$(($YEAR_NOW + 1))
+ fi
+ WAKEUPTIME=$((`date -d "$YEAR_NOW-01-01 $DREAMTIME:00" +%s` + $DREAMDAY * 3600 * 24))
+elif [ -n "$MAX_POWEROFF_TIME" ] && (($MAX_POWEROFF_TIME > 0)); then # calculate, at what time the machine should be powered on
+ WAKEUPTIME=$((`date +%s` + $MAX_POWEROFF_TIME * 60))
+else
+ WAKEUPTIME=$1
+fi
+
+# final test
+if [ $WAKEUPTIME -gt $1 ] && [ $1 -gt 0 ]; then
+ WAKEUPTIME=$1
+fi
+
+# wakeupbefore - minutes before the programmed wakeuptime the computer should wake up
+[ -z "$WAKEUP_BEFORE" ] && WAKEUP_BEFORE=0
+WAKEUPTIME=$(($WAKEUPTIME - $WAKEUP_BEFORE * 60))
+
+# wakeup-time is greater then zero
+if [ $WAKEUPTIME -gt 0 ]; then
+ # calculate the minimal wakeup-time
+ MinEventTimeout=`grep -m1 "MinEventTimeout" $VDRCONFDIR/setup.conf | cut -f 2 -d "=" | sed -e 's/^ //g' -e 's/ $//g'`
+ [ $MinEventTimeout -lt 10 ] && MinEventTimeout=10
+ MIN_WAKEUPTIME=$(($NOW + $MinEventTimeout * 60 + 1))
+ if [ $MIN_WAKEUPTIME -gt $WAKEUPTIME ]; then
+ case "$MIN_WAKEUP_VAR" in
+ 0) VAR=`date -d "1970-01-01 UTC $MIN_WAKEUPTIME seconds" +"%Y-%m-%d %R:%S"`
+ echo "$VDRBINDIR/svdrpsend.pl MESG 'Timer wird auf $VAR gestellt.'"| at now
+ sleep 2
+ ;;
+ 1) WAKEUPTIME=$(($WAKEUPTIME - $MinEventTimeout * 60 + 600))
+ [ $WAKEUPTIME -lt $(($NOW + 600)) ] && WAKEUPTIME=$(($NOW + 600))
+ VAR=`date -d "1970-01-01 UTC $WAKEUPTIME seconds" +"%Y-%m-%d %R:%S"`
+ echo "$VDRBINDIR/svdrpsend.pl MESG 'Timer wird auf $VAR gestellt.'"| at now
+ sleep 2
+ ;;
+ 2) echo "$VDRBINDIR/svdrpsend.pl MESG 'Es werden keine Timer gestellt.'"| at now
+ sleep 2
+ [ "$WAKEUP_MODE" = "1" ] && sudo $VDRSCRIPTDIR/vdr2root poweroff && exit 0
+ sudo $VDRSCRIPTDIR/vdr2root shutdown && exit 0
+ ;;
+ 3) echo "$VDRBINDIR/svdrpsend.pl MESG 'Shutdown abgebrochen.'"| at now
+ sleep 2
+ exit 0
+ ;;
+ esac
+ fi
+
+ # debug
+ if [ "$DEBUG" = "1" ]; then
+ echo `date '+%F %T'` > /tmp/wakeuptime.txt
+ echo "Next Wakeup Time:" >> /tmp/wakeuptime.txt
+ echo "ACPI -> `date -d "1970-01-01 UTC $WAKEUPTIME seconds" +"%Y-%m-%d %R:%S"` > $ACPI" >> /tmp/wakeuptime.txt
+ echo "NVRAM -> $WAKEUPTIME" >> /tmp/wakeuptime.txt
+ fi
+
+ # acpi-wakeup
+ if [ "$WAKEUP_MODE" = "1" ]; then
+ echo "$VDRBINDIR/svdrpsend.pl MESG 'Shutdown now!'"| at now
+ if [ "$ACPI" = "/sys/class/rtc/rtc0/wakealarm" ]; then
+ VAR="$WAKEUPTIME"
+ sudo $VDRSCRIPTDIR/vdr2root acpi-wakeup "0" "$ACPI"
+ else
+ VAR=`date -d "1970-01-01 UTC $WAKEUPTIME seconds" +"%Y-%m-%d %R:%S"`
+ sudo $VDRSCRIPTDIR/vdr2root acpi-wakeup "$VAR" "$ACPI"
+ fi
+ sudo $VDRSCRIPTDIR/vdr2root acpi-wakeup "$VAR" "$ACPI"
+ sleep 1
+ sudo $VDRSCRIPTDIR/vdr2root poweroff
+ exit 0
+ fi
+
+ # nvram-wakeup
+ if [ "$WAKEUP_MODE" = "2" ]; then
+ echo "$VDRBINDIR/svdrpsend.pl MESG 'Shutdown now!'"| at now
+ VAR="-s $WAKEUPTIME $NVRAM_OPT ${NVRAM_CONFIG:+-C $NVRAM_CONFIG} ${NVRAM_IWNAME:+-I $NVRAM_IWNAME}"
+# VAR="-s $WAKEUPTIME $NVRAM_OPT -w 0 ${NVRAM_CONFIG:+-C $NVRAM_CONFIG} ${NVRAM_IWNAME:+-I $NVRAM_IWNAME}"
+# sudo $VDRSCRIPTDIR/vdr2root nvram-wakeup -d
+ sudo $VDRSCRIPTDIR/vdr2root nvram-wakeup "$VAR"
+ sleep 1
+ if [ "$NEED_REBOOT" = "0" ]; then
+ sudo $VDRSCRIPTDIR/vdr2root shutdown
+ else
+ if [ "$LILO" = "1" ]; then
+ sudo $VDRSCRIPTDIR/vdr2root lilo
+ else
+ sudo $VDRSCRIPTDIR/vdr2root grub $GRUB_POWEROFF
+ fi
+ sudo $VDRSCRIPTDIR/vdr2root reboot
+ fi
+ exit 0
+ fi
+
+else # wakeup-time is equal to zero
+ if [ "$WAKEUP_MODE" = "2" ]; then
+ # disable nvram-wakeup
+ sudo $VDRSCRIPTDIR/vdr2root nvram-wakeup -d
+ sleep 1
+ # if wakeup-lan works only with a shutdown via poweroff-kernel
+ if [ "$NEED_REBOOT" = "2" ]; then
+ if [ "$LILO" = "1" ]; then
+ sudo $VDRSCRIPTDIR/vdr2root lilo
+ else
+ sudo $VDRSCRIPTDIR/vdr2root grub $GRUB_POWEROFF
+ fi
+ sudo $VDRSCRIPTDIR/vdr2root reboot
+ exit 0
+ fi
+ fi
+fi
+
+# wakeuptime is equal to zero, shutdown
+echo "$VDRBINDIR/svdrpsend.pl MESG 'Shutdown now!'"| at now
+if [ "$WAKEUP_MODE" = "1" ]; then
+ sudo $VDRSCRIPTDIR/vdr2root poweroff
+else
+ sudo $VDRSCRIPTDIR/vdr2root shutdown
+fi
+
+exit 0
diff --git a/vdr/scripts/vdrwritedvd b/vdr/scripts/vdrwritedvd
new file mode 100644
index 0000000..2c4ad1c
--- /dev/null
+++ b/vdr/scripts/vdrwritedvd
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# vdrwritedvd - x-vdr version
+#
+# This script will be called by the vdr-plugins dvdselect and dvdswitch to write a dvd.
+#
+# It gets the following parameter:
+#
+# $1 = filename
+
+# Load VDR configuration
+. /etc/default/vdr
+
+# vdr2root
+echo "sudo $VDRSCRIPTDIR/vdr2root writedvd \"$1\"" | at now
+
+exit 0
diff --git a/vdr/scripts/x-lcd b/vdr/scripts/x-lcd
new file mode 100644
index 0000000..ef28a7c
--- /dev/null
+++ b/vdr/scripts/x-lcd
@@ -0,0 +1,26 @@
+#! /bin/sh
+# /etc/init.d/x-lcd
+
+# Load VDR configuration
+. /etc/default/vdr
+
+STARTLOGO=$VDRCONFDIR/plugins/graphlcd/splash/start
+STOPLOGO=$VDRCONFDIR/plugins/graphlcd/splash/stop
+
+case "$1" in
+ start)
+ echo "Displaying Startlogo"
+ /usr/bin/showpic -c /etc/graphlcd.conf -d ks0108 -s 200 $(ls $STARTLOGO/*.glcd) &
+ ;;
+ stop)
+ echo "Displaying Shutdownlogo"
+ /usr/bin/showpic -c /etc/graphlcd.conf -d ks0108 -s 200 $(ls $STOPLOGO/*.glcd) &
+ ;;
+ *)
+ echo "Usage: /etc/init.d/x-lcd {start|stop}"
+ exit 1
+ ;;
+esac
+
+exit 0
+
diff --git a/vdr/vdr-1.7.5-ext_s2apiwrapper-0.5.diff b/vdr/vdr-1.7.5-ext_s2apiwrapper-0.5.diff
new file mode 100644
index 0000000..b10be7a
--- /dev/null
+++ b/vdr/vdr-1.7.5-ext_s2apiwrapper-0.5.diff
@@ -0,0 +1,475 @@
+diff -ruNp vdr-1.7.5-extensions/channels.c vdr-1.7.5-ext-s2apiwrapper/channels.c
+--- vdr-1.7.5-extensions/channels.c 2009-04-12 13:37:59.000000000 +0200
++++ vdr-1.7.5-ext-s2apiwrapper/channels.c 2009-04-13 21:06:29.000000000 +0200
+@@ -11,6 +11,7 @@
+ #include <linux/dvb/frontend.h>
+ #include <ctype.h>
+ #include "device.h"
++#include "s2apiwrapper.h"
+ #include "epg.h"
+ #include "timers.h"
+ #ifdef USE_STREAMDEVEXT
+diff -ruNp vdr-1.7.5-extensions/dvbdevice.c vdr-1.7.5-ext-s2apiwrapper/dvbdevice.c
+--- vdr-1.7.5-extensions/dvbdevice.c 2009-04-12 13:37:59.000000000 +0200
++++ vdr-1.7.5-ext-s2apiwrapper/dvbdevice.c 2009-04-13 21:06:29.000000000 +0200
+@@ -230,7 +230,7 @@ bool cDvbTuner::SetFrontend(void)
+ memset(&CmdSeq, 0, sizeof(CmdSeq));
+ CmdSeq.props = Frontend;
+ SETCMD(DTV_CLEAR, 0);
+- if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
++ if (S2API_ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
+ esyslog("ERROR: frontend %d: %m", cardIndex);
+ return false;
+ }
+@@ -364,7 +364,7 @@ bool cDvbTuner::SetFrontend(void)
+ return false;
+ }
+ SETCMD(DTV_TUNE, 0);
+- if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
++ if (S2API_ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
+ esyslog("ERROR: frontend %d: %m", cardIndex);
+ return false;
+ }
+@@ -1428,12 +1428,14 @@ int cDvbDevice::PlayAudio(const uchar *D
+
+ int cDvbDevice::PlayTsVideo(const uchar *Data, int Length)
+ {
+- return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
++ //return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
++ return cDevice::PlayTsVideo(Data, Length);
+ }
+
+ int cDvbDevice::PlayTsAudio(const uchar *Data, int Length)
+ {
+- return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
++ //return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
++ return cDevice::PlayTsAudio(Data, Length);
+ }
+
+ bool cDvbDevice::OpenDvr(void)
+diff -ruNp vdr-1.7.5-extensions/dvbdevice.h vdr-1.7.5-ext-s2apiwrapper/dvbdevice.h
+--- vdr-1.7.5-extensions/dvbdevice.h 2009-04-12 13:37:59.000000000 +0200
++++ vdr-1.7.5-ext-s2apiwrapper/dvbdevice.h 2009-04-13 21:06:29.000000000 +0200
+@@ -14,9 +14,10 @@
+ #include <linux/dvb/version.h>
+ #include "device.h"
+ #include "dvbspu.h"
+-
+-#if DVB_API_VERSION != 5 || DVB_API_VERSION_MINOR != 0
+-#error VDR requires Linux DVB driver API version 5.0!
++#include "s2apiwrapper.h"
++
++#if DVB_API_VERSION != 3
++#error VDR requires Linux DVB driver API version 3!
+ #endif
+
+ #define MAXDVBDEVICES 8
+diff -ruNp vdr-1.7.5-extensions/Makefile vdr-1.7.5-ext-s2apiwrapper/Makefile
+--- vdr-1.7.5-extensions/Makefile 2009-04-12 13:37:59.000000000 +0200
++++ vdr-1.7.5-ext-s2apiwrapper/Makefile 2009-04-13 21:06:29.000000000 +0200
+@@ -36,7 +36,7 @@ DOXYFILE = Doxyfile
+
+ SILIB = $(LSIDIR)/libsi.a
+
+-OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o dvbosd.o\
++OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o s2apiwrapper.o dvbdevice.o dvbci.o dvbosd.o\
+ dvbplayer.o dvbspu.o dvbsubtitle.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
+ lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\
+ receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o shutdown.o\
+diff -ruNp vdr-1.7.5-extensions/nit.c vdr-1.7.5-ext-s2apiwrapper/nit.c
+--- vdr-1.7.5-extensions/nit.c 2008-12-20 11:57:50.000000000 +0100
++++ vdr-1.7.5-ext-s2apiwrapper/nit.c 2009-04-13 21:06:29.000000000 +0200
+@@ -10,6 +10,7 @@
+ #include "nit.h"
+ #include <linux/dvb/frontend.h>
+ #include "channels.h"
++#include "s2apiwrapper.h"
+ #include "eitscan.h"
+ #include "libsi/section.h"
+ #include "libsi/descriptor.h"
+diff -ruNp vdr-1.7.5-extensions/s2apiwrapper.c vdr-1.7.5-ext-s2apiwrapper/s2apiwrapper.c
+--- vdr-1.7.5-extensions/s2apiwrapper.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-ext-s2apiwrapper/s2apiwrapper.c 2009-04-13 21:06:29.000000000 +0200
+@@ -0,0 +1,165 @@
++/*
++ * s2apiwrapper.c:
++ * Wrapper to translate DVB S2API to DVB 3.0 API calls
++ *
++ * See the main source file 'vdr.c' for copyright information and
++ * how to reach the author.
++ *
++ * $Id: $
++ */
++
++#include "s2apiwrapper.h"
++#include <string.h>
++#include <errno.h>
++
++#ifdef DVB_S2API_WRAPPER
++
++static int ioctl_FE_SET_PROPERTY(int d, dtv_properties *properties) {
++ int result = 0;
++ fe_delivery_system Delivery = SYS_UNDEFINED;
++ dvb_frontend_parameters Frontend;
++ memset(&Frontend, 0, sizeof(Frontend));
++
++ for (__u32 i=0; i<properties->num; i++) {
++ __u32 cmd = properties->props[i].cmd;
++ __u32 data = properties->props[i].u.data;
++
++ switch (cmd) {
++ case DTV_CLEAR:
++ memset(&Frontend, 0, sizeof(Frontend));
++ continue;
++
++ case DTV_DELIVERY_SYSTEM:
++ Delivery = fe_delivery_system(data);
++ continue;
++
++ case DTV_FREQUENCY:
++ Frontend.frequency = data;
++ continue;
++
++ case DTV_INVERSION:
++ Frontend.inversion = fe_spectral_inversion(data);
++ continue;
++
++ case DTV_TUNE:
++ result = ioctl(d, FE_SET_FRONTEND, &Frontend);
++ if (result < 0)
++ return result;
++ continue;
++ }
++
++ if (Delivery == SYS_DVBS) {
++ switch (cmd) {
++ case DTV_MODULATION:
++ if (data != FE_QPSK) {
++ errno = EINVAL;
++ return -1;
++ }
++ break;
++
++ case DTV_SYMBOL_RATE:
++ Frontend.u.qpsk.symbol_rate = data;
++ break;
++
++ case DTV_INNER_FEC:
++ Frontend.u.qpsk.fec_inner = fe_code_rate(data);
++ break;
++
++ case DTV_ROLLOFF:
++ if (data != ROLLOFF_35) {
++ errno = EINVAL;
++ return -1;
++ }
++ break;
++ default:
++ errno = EINVAL;
++ return -1;
++ }
++ }
++ else if (Delivery == SYS_DVBC_ANNEX_AC && result >= 0) {
++ switch (cmd) {
++ case DTV_MODULATION:
++ Frontend.u.qam.modulation = fe_modulation(data);
++ break;
++
++ case DTV_SYMBOL_RATE:
++ Frontend.u.qam.symbol_rate = data;
++ break;
++
++ case DTV_INNER_FEC:
++ Frontend.u.qam.fec_inner = fe_code_rate(data);
++ break;
++ default:
++ errno = EINVAL;
++ return -1;
++ }
++ }
++ else if (Delivery == SYS_DVBT && result >= 0) {
++ switch (cmd) {
++ case DTV_MODULATION:
++ Frontend.u.ofdm.constellation = fe_modulation(data);
++ break;
++
++ case DTV_BANDWIDTH_HZ:
++ Frontend.u.ofdm.bandwidth = fe_bandwidth(data);
++ break;
++
++ case DTV_CODE_RATE_HP:
++ Frontend.u.ofdm.code_rate_HP = fe_code_rate(data);
++ break;
++
++ case DTV_CODE_RATE_LP:
++ Frontend.u.ofdm.code_rate_LP = fe_code_rate(data);
++ break;
++
++ case DTV_TRANSMISSION_MODE:
++ Frontend.u.ofdm.transmission_mode = fe_transmit_mode(data);
++ break;
++
++ case DTV_GUARD_INTERVAL:
++ Frontend.u.ofdm.guard_interval = fe_guard_interval(data);
++ break;
++
++ case DTV_HIERARCHY:
++ Frontend.u.ofdm.hierarchy_information = fe_hierarchy(data);
++ break;
++
++ default:
++ errno = EINVAL;
++ return -1;
++ }
++ }
++ } // for
++ return result;
++}
++
++static int ioctl_FE_GET_PROPERTY(int d, dtv_properties *properties) {
++ errno = EINVAL;
++ return -1;
++}
++
++#ifdef DVB_S2API_RUNTIME
++static bool S2APITested = false;
++static bool HasS2APIResult;
++
++static bool HasS2API(int d) {
++ if (!S2APITested) {
++ // TODO: Test for S2API availability
++ HasS2APIResult = false;
++ S2APITested = true;
++ }
++ return HasS2APIResult;
++}
++#else
++static bool HasS2API(int d) { return false; }
++#endif // ifdef DVB_S2API_RUNTIME
++
++int S2API_ioctl(int d, int request, void *data) {
++ if (request == (int)FE_SET_PROPERTY && !HasS2API(d))
++ return ioctl_FE_SET_PROPERTY(d, (dtv_properties*)data);
++ if (request == (int)FE_GET_PROPERTY && !HasS2API(d))
++ return ioctl_FE_GET_PROPERTY(d, (dtv_properties*)data);
++ return ioctl(d, request, data);
++}
++
++#endif // ifdef DVB_S2API_WRAPPER
+diff -ruNp vdr-1.7.5-extensions/s2apiwrapper.h vdr-1.7.5-ext-s2apiwrapper/s2apiwrapper.h
+--- vdr-1.7.5-extensions/s2apiwrapper.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.5-ext-s2apiwrapper/s2apiwrapper.h 2009-04-13 21:06:29.000000000 +0200
+@@ -0,0 +1,213 @@
++/*
++ * s2apiwrapper.h:
++ * Wrapper to translate DVB S2API to DVB 3.0 API calls
++ *
++ * See the main source file 'vdr.c' for copyright information and
++ * how to reach the author.
++ *
++ * $Id: $
++ */
++
++#ifndef __S2API_WRAPPER_H
++#define __S2API_WRAPPER_H
++
++#include <linux/dvb/frontend.h>
++#include <linux/dvb/version.h>
++#include <sys/ioctl.h>
++
++#if DVB_API_VERSION != 3
++#error VDR requires Linux DVB driver API version 3!
++#endif
++
++#if !defined(DVB_S2API_RUNTIME)
++#define DVB_S2API_RUNTIME 1
++#endif
++
++#if DVB_S2API_RUNTIME == 0
++#undef DVB_S2API_RUNTIME
++#endif
++
++#if !defined(DVB_S2API_WRAPPER) \
++ && (DVB_API_VERSION_MINOR < 3 || defined(DVB_S2API_RUNTIME))
++#define DVB_S2API_WRAPPER 1
++#endif
++
++#if defined(DVB_S2API_WRAPPER) && DVB_S2API_WRAPPER == 0
++#undef DVB_S2API_WRAPPER
++#endif
++
++#if DVB_API_VERSION_MINOR < 3
++
++// DVB API is missing S2API structs, so we declare them.
++// The following is based on frontend.h from s2api DVB driver
++
++
++enum fe_s2caps {
++ FE_HAS_EXTENDED_CAPS = 0x800000, // We need more bitspace for newer APIs, indicate this.
++ FE_CAN_2G_MODULATION = 0x10000000, // frontend supports "2nd generation modulation" (DVB-S2)
++};
++
++enum fe_s2code_rate {
++ FEC_3_5 = FEC_AUTO + 1,
++ FEC_9_10,
++};
++
++enum fe_s2modulation {
++ PSK_8 = VSB_16 + 1,
++ APSK_16,
++ APSK_32,
++ DQPSK,
++};
++
++
++/* S2API Commands */
++#define DTV_UNDEFINED 0
++#define DTV_TUNE 1
++#define DTV_CLEAR 2
++#define DTV_FREQUENCY 3
++#define DTV_MODULATION 4
++#define DTV_BANDWIDTH_HZ 5
++#define DTV_INVERSION 6
++#define DTV_DISEQC_MASTER 7
++#define DTV_SYMBOL_RATE 8
++#define DTV_INNER_FEC 9
++#define DTV_VOLTAGE 10
++#define DTV_TONE 11
++#define DTV_PILOT 12
++#define DTV_ROLLOFF 13
++#define DTV_DISEQC_SLAVE_REPLY 14
++
++/* Basic enumeration set for querying unlimited capabilities */
++#define DTV_FE_CAPABILITY_COUNT 15
++#define DTV_FE_CAPABILITY 16
++#define DTV_DELIVERY_SYSTEM 17
++
++#if 0
++/* ISDB */
++/* maybe a dup of DTV_ISDB_SOUND_BROADCASTING_SUBCHANNEL_ID ??? */
++#define DTV_ISDB_SEGMENT_IDX 18
++/* 1, 3 or 13 ??? */
++#define DTV_ISDB_SEGMENT_WIDTH 19
++
++/* the central segment can be received independently or 1/3 seg in SB-mode */
++#define DTV_ISDB_PARTIAL_RECEPTION 20
++/* sound broadcasting is used 0 = 13segment, 1 = 1 or 3 see DTV_ISDB_PARTIAL_RECEPTION */
++#define DTV_ISDB_SOUND_BROADCASTING 21
++
++/* only used in SB */
++/* determines the initial PRBS of the segment (to match with 13seg channel) */
++#define DTV_ISDB_SOUND_BROADCASTING_SUBCHANNEL_ID 22
++
++#define DTV_ISDB_LAYERA_FEC 23
++#define DTV_ISDB_LAYERA_MODULATION 24
++#define DTV_ISDB_LAYERA_SEGMENT_WIDTH 25
++#define DTV_ISDB_LAYERA_TIME_INTERLEAVER 26
++
++#define DTV_ISDB_LAYERB_FEC 27
++#define DTV_ISDB_LAYERB_MODULATION 28
++#define DTV_ISDB_LAYERB_SEGMENT_WIDTH 29
++#define DTV_ISDB_LAYERB_TIME_INTERLEAVING 30
++
++#define DTV_ISDB_LAYERC_FEC 31
++#define DTV_ISDB_LAYERC_MODULATION 32
++#define DTV_ISDB_LAYERC_SEGMENT_WIDTH 33
++#define DTV_ISDB_LAYERC_TIME_INTERLEAVING 34
++#endif
++#define DTV_API_VERSION 35
++#define DTV_API_VERSION 35
++#define DTV_CODE_RATE_HP 36
++#define DTV_CODE_RATE_LP 37
++#define DTV_GUARD_INTERVAL 38
++#define DTV_TRANSMISSION_MODE 39
++#define DTV_HIERARCHY 40
++
++#define DTV_MAX_COMMAND DTV_HIERARCHY
++
++typedef enum fe_pilot {
++ PILOT_ON,
++ PILOT_OFF,
++ PILOT_AUTO,
++} fe_pilot_t;
++
++typedef enum fe_rolloff {
++ ROLLOFF_35, /* Implied value in DVB-S, default for DVB-S2 */
++ ROLLOFF_20,
++ ROLLOFF_25,
++ ROLLOFF_AUTO,
++} fe_rolloff_t;
++
++typedef enum fe_delivery_system {
++ SYS_UNDEFINED,
++ SYS_DVBC_ANNEX_AC,
++ SYS_DVBC_ANNEX_B,
++ SYS_DVBT,
++ SYS_DSS,
++ SYS_DVBS,
++ SYS_DVBS2,
++ SYS_DVBH,
++ SYS_ISDBT,
++ SYS_ISDBS,
++ SYS_ISDBC,
++ SYS_ATSC,
++ SYS_ATSCMH,
++ SYS_DMBTH,
++ SYS_CMMB,
++ SYS_DAB,
++} fe_delivery_system_t;
++
++struct dtv_cmds_h {
++ char *name; /* A display name for debugging purposes */
++
++ __u32 cmd; /* A unique ID */
++
++ /* Flags */
++ __u32 set:1; /* Either a set or get property */
++ __u32 buffer:1; /* Does this property use the buffer? */
++ __u32 reserved:30; /* Align */
++};
++
++struct dtv_property {
++ __u32 cmd;
++ __u32 reserved[3];
++ union {
++ __u32 data;
++ struct {
++ __u8 data[32];
++ __u32 len;
++ __u32 reserved1[3];
++ void *reserved2;
++ } buffer;
++ } u;
++ int result;
++} __attribute__ ((packed));
++
++/* num of properties cannot exceed DTV_IOCTL_MAX_MSGS per ioctl */
++#define DTV_IOCTL_MAX_MSGS 64
++
++struct dtv_properties {
++ __u32 num;
++ struct dtv_property *props;
++};
++
++#define FE_SET_PROPERTY _IOW('o', 82, struct dtv_properties)
++#define FE_GET_PROPERTY _IOR('o', 83, struct dtv_properties)
++
++// End of copied section of frontend.h
++
++#endif // DVB_API_VERSION_MINOR < 3
++
++
++
++#ifdef DVB_S2API_WRAPPER
++
++// Wrapper for S2API ioctl calls:
++int S2API_ioctl(int d, int request, void *data);
++
++#else // ifdef DVB_S2API_WRAPPER
++
++// Null wrapper for s2api ioctl calls:
++inline int S2API_ioctl(int d, int request, void *data) { return ioctl(d, request, data); }
++
++#endif // ifdef DVB_S2API_WRAPPER
++
++#endif // ifndef __S2API_WRAPPER_H